Wrapper helper (#1799)

* [WRAPPERHELPER] Added wrapperhelper v0.1, tested on libc

* [WRAPPED] Removed updates to libc from this branch

* [WRAPPERHELPER] Removed GPL header and added modified LGPL header, added notes about licensing issues
This commit is contained in:
rajdakin 2024-09-06 15:07:38 +02:00 committed by GitHub
parent 7dc59ac342
commit 6044feb7fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 11608 additions and 2326 deletions

12
.gitignore vendored
View File

@ -60,11 +60,13 @@ build*/
src/git_head.h
backup/
# LLVMprivateGenerator
/LLVMprivateGenerator/*
!/LLVMprivateGenerator/Makefile
!/LLVMprivateGenerator/main.cpp
!/LLVMprivateGenerator/registered_structs.cpp
# Wrapper helper
/wrapperhelper/bin
/wrapperhelper/makedir
/wrapperhelper/obj
/wrapperhelper/sanaddress
/wrapperhelper/sanleak
/wrapperhelper/sanundefined
# macOS
.DS_Store

View File

@ -1,12 +0,0 @@
all: dumpSigs
dumpSigs: main.o registered_structs.o
g++ -g3 -std=gnu++17 -fno-rtti main.o registered_structs.o -o dumpSigs "-L$(LLVM_install_dir)/lib" -lclang-cpp -lclangTooling -Wl,-rpath "-Wl,$(LLVM_install_dir)/lib"
main.o: main.cpp
g++ -g3 -std=gnu++17 -fno-rtti -c main.cpp -Wfatal-errors "-I$(LLVM_install_dir)/include" -o main.o
registered_structs.o: registered_structs.cpp
g++ -g3 -std=gnu++17 -fno-rtti -c registered_structs.cpp -Wfatal-errors -o registered_structs.o
clean:
$(RM) dumpSigs main.o registered_structs.o

View File

@ -1,458 +0,0 @@
#include <algorithm>
#include <fstream>
#include <iostream>
#include <numeric>
#include <unordered_map>
#include <utility>
#include <vector>
#include <llvm/Support/raw_ostream.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Decl.h>
#include <clang/AST/Mangle.h>
#include <clang/AST/PrettyPrinter.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Frontend/FrontendActions.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <clang/Tooling/Tooling.h>
clang::MangleContext *mangler = nullptr;
std::unordered_map<std::string, std::pair<std::string, bool>> funMap;
std::vector<std::string> funList;
bool isTypeTrivial(const clang::QualType &q, const clang::QualType *qorig);
clang::QualType getPointedType(const clang::QualType q) {
if (const clang::PointerType *p = q->getAs<clang::PointerType>())
return getPointedType(p->getPointeeType());
else if (const clang::ReferenceType *r = q->getAs<clang::ReferenceType>())
return getPointedType(r->getPointeeType());
else
return q;
}
std::string record2name(const clang::RecordDecl &q, const clang::QualType *qorig) {
std::string s = q.getNameAsString();
if (s == "") {
if (!qorig) return "????.!";
if (const clang::TypedefType *tt = (*qorig)->getAs<clang::TypedefType>()) {
// Typedef
if (clang::TypedefNameDecl *td = tt->getDecl()) {
return td->getNameAsString();
} else {
return "<typedef with no declaration>";
}
} else {
return std::string("<unknown type ") + (*qorig)->getTypeClassName() + ">";
}
} else {
return s;
}
}
char ptr2char(const std::string &str) __attribute__((const));
const char *ptr2str(const std::string &str) __attribute__((const));
char type2char(const clang::QualType &qual /* Canonical */, const clang::QualType *qorig) {
if (qual->isBuiltinType()) {
switch (static_cast<const clang::BuiltinType&>(*qual).getKind()) {
case clang::BuiltinType::Kind::Void:
return 'v';
case clang::BuiltinType::Kind::Bool:
return 'i';
case clang::BuiltinType::Kind::Char_U:
return 'C';
case clang::BuiltinType::Kind::Char_S:
return 'c';
case clang::BuiltinType::Kind::Char8:
return 'c';
case clang::BuiltinType::Kind::UChar:
return 'C';
case clang::BuiltinType::Kind::SChar:
return 'c';
case clang::BuiltinType::Kind::WChar_U:
return 'W';
case clang::BuiltinType::Kind::UShort:
return 'W';
case clang::BuiltinType::Kind::WChar_S:
return 'w';
case clang::BuiltinType::Kind::Char16:
return 'w';
case clang::BuiltinType::Kind::Short:
return 'w';
case clang::BuiltinType::Kind::UInt:
return 'u';
case clang::BuiltinType::Kind::Char32:
return 'i';
case clang::BuiltinType::Kind::Int:
return 'i';
case clang::BuiltinType::Kind::ULong:
return 'L';
case clang::BuiltinType::Kind::Long:
return 'l';
case clang::BuiltinType::Kind::ULongLong:
return 'U';
case clang::BuiltinType::Kind::LongLong:
return 'I';
case clang::BuiltinType::Kind::UInt128:
return 'H';
case clang::BuiltinType::Kind::Int128:
return 'H';
case clang::BuiltinType::Kind::Float:
return 'f';
case clang::BuiltinType::Kind::Double:
return 'd';
case clang::BuiltinType::Kind::LongDouble:
return 'D';
case clang::BuiltinType::Kind::NullPtr:
return 'p'; // nullptr_t
case clang::BuiltinType::Kind::Half:
case clang::BuiltinType::Kind::BFloat16:
case clang::BuiltinType::Kind::ShortAccum:
case clang::BuiltinType::Kind::Accum:
case clang::BuiltinType::Kind::LongAccum:
case clang::BuiltinType::Kind::UShortAccum:
case clang::BuiltinType::Kind::UAccum:
case clang::BuiltinType::Kind::ULongAccum:
case clang::BuiltinType::Kind::ShortFract:
case clang::BuiltinType::Kind::Fract:
case clang::BuiltinType::Kind::LongFract:
case clang::BuiltinType::Kind::UShortFract:
case clang::BuiltinType::Kind::UFract:
case clang::BuiltinType::Kind::ULongFract:
case clang::BuiltinType::Kind::SatShortAccum:
case clang::BuiltinType::Kind::SatAccum:
case clang::BuiltinType::Kind::SatLongAccum:
case clang::BuiltinType::Kind::SatUShortAccum:
case clang::BuiltinType::Kind::SatUAccum:
case clang::BuiltinType::Kind::SatULongAccum:
case clang::BuiltinType::Kind::SatShortFract:
case clang::BuiltinType::Kind::SatFract:
case clang::BuiltinType::Kind::SatLongFract:
case clang::BuiltinType::Kind::SatUShortFract:
case clang::BuiltinType::Kind::SatUFract:
case clang::BuiltinType::Kind::SatULongFract:
case clang::BuiltinType::Kind::Float16:
case clang::BuiltinType::Kind::Float128:
case clang::BuiltinType::Kind::Overload:
case clang::BuiltinType::Kind::BoundMember:
case clang::BuiltinType::Kind::PseudoObject:
case clang::BuiltinType::Kind::Dependent:
case clang::BuiltinType::Kind::UnknownAny:
case clang::BuiltinType::Kind::ARCUnbridgedCast:
case clang::BuiltinType::Kind::BuiltinFn:
case clang::BuiltinType::Kind::ObjCId:
case clang::BuiltinType::Kind::ObjCClass:
case clang::BuiltinType::Kind::ObjCSel:
#define IMAGE_TYPE(it, id, si, a, s) case clang::BuiltinType::Kind::id:
#include <clang/Basic/OpenCLImageTypes.def>
#undef IMAGE_TYPE
case clang::BuiltinType::Kind::OCLSampler:
case clang::BuiltinType::Kind::OCLEvent:
case clang::BuiltinType::Kind::OCLClkEvent:
case clang::BuiltinType::Kind::OCLQueue:
case clang::BuiltinType::Kind::OCLReserveID:
case clang::BuiltinType::Kind::IncompleteMatrixIdx:
case clang::BuiltinType::Kind::OMPArraySection:
case clang::BuiltinType::Kind::OMPArrayShaping:
case clang::BuiltinType::Kind::OMPIterator:
#define EXT_OPAQUE_TYPE(et, id, e) case clang::BuiltinType::Kind::id:
#include <clang/Basic/OpenCLExtensionTypes.def>
#define SVE_TYPE(n, id, si) case clang::BuiltinType::Kind::id:
#include <clang/Basic/AArch64SVEACLETypes.def>
#define PPC_VECTOR_TYPE(n, id, s) case clang::BuiltinType::Kind::id:
#include <clang/Basic/PPCTypes.def>
#undef EXT_OPAQUE_TYPE
#undef SVE_TYPE
#undef PPC_VECTOR_TYPE
return '!';
default:
return ':';
}
} else if (qual->isEnumeralType()) {
const clang::EnumDecl *ed = qual->getAs<clang::EnumType>()->getDecl();
if (!ed) {
return 'i';
} else {
return type2char(ed->getIntegerType().getCanonicalType(), qorig);
}
} else if (qual->isFunctionPointerType()) {
return '@';
} else if (qual->isAnyPointerType() || qual->isReferenceType()) {
const clang::QualType &pointed = getPointedType(qual);
if (isTypeTrivial(pointed, qorig)) {
return 'p';
} else if (const clang::RecordType *rct = pointed->getAs<clang::RecordType>()) {
clang::RecordDecl *rc = rct->getDecl();
if (!rc) {
return '!';
} else if (!rc->isCompleteDefinition()) {
return 'p';
} else {
std::string str;
if (qorig) {
const clang::QualType qpted = getPointedType(*qorig);
str = record2name(*rc, &qpted);
} else {
str = record2name(*rc, nullptr);
}
char ret = ptr2char(str);
if (ret) return ret;
else {
return '!';
}
}
} else {
return '!';
}
} else if (const clang::RecordType *rct = qual->getAs<clang::RecordType>()) {
clang::RecordDecl *rc = rct->getDecl();
if (!rc) {
return '?';
} else if (rc->getNameAsString() == "__builtin_va_list") {
// va_list
return 'A';
} else {
return '?';
}
} else {
return '?';
}
}
bool isTypeTrivial(const clang::QualType &q, const clang::QualType *qorig) {
const char c = type2char(q, qorig);
#define GO(chr) || (c == chr)
return (c == 'v')
GO('i') GO('u')
GO('I') GO('U')
GO('l') GO('L')
GO('f') GO('d')
GO('D') GO('K')
GO('0') GO('1')
GO('C') GO('c')
GO('W') GO('w')
GO('H')
GO('p');
#undef GO
}
bool isTypeValid(const clang::QualType &q, const clang::QualType *qorig) {
const char c = type2char(q, qorig);
if (c == 'A') return false;
if (c == 'V') return false;
return ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
}
const std::string type2string(const clang::QualType &qual, const clang::QualType *qorig) {
if (qual->isBuiltinType()) {
return std::string("(builtin) ") + static_cast<const clang::BuiltinType&>(*qual).getName(clang::PrintingPolicy{{}}).data();
} else if (qual->isFunctionPointerType()) {
return "Callback (function pointer)";
} else if (qual->isAnyPointerType() || qual->isReferenceType()) {
std::string prefix = qual->isAnyPointerType() ? "Pointer to " : "Reference to ";
const clang::QualType &pointed = getPointedType(qual);
if (isTypeTrivial(pointed, qorig)) {
return prefix + "trivial object " + type2string(pointed, qorig) + " (" + type2char(pointed, qorig) + ")";
} else if (const clang::RecordType *rct = pointed->getAs<clang::RecordType>()) {
clang::RecordDecl *rc = rct->getDecl();
if (!rc) {
return prefix + "unknown record";
} else if (!rc->isCompleteDefinition()) {
return prefix + "incomplete record " + rc->getNameAsString();
} else {
std::string str;
if (qorig) {
const clang::QualType qpted = getPointedType(*qorig);
str = record2name(*rc, &qpted);
} else {
str = record2name(*rc, nullptr);
}
const char *ret = ptr2str(str);
if (ret[0] != '\0') {
return prefix + ret;
} else {
if (mangler && mangler->shouldMangleDeclName(rc)) {
std::string mangled;
{
llvm::raw_string_ostream strstr{mangled};
mangler->mangleName(rc, strstr);
}
return prefix + "unknown record " + str + " (=== " + mangled + ")";
} else {
return prefix + "unknown record " + str;
}
}
}
} else {
return prefix + "non-trivial or typedef'ed object " + type2string(pointed, qorig) + " (" + type2char(pointed, qorig) + ")";
//return "Pointer (maybe to callback)";
}
} else if (qual->isEnumeralType()) {
const clang::EnumDecl *ed = qual->getAs<clang::EnumType>()->getDecl();
if (!ed) {
return "Enumeration with unknown underlying integer type (assuming int)";
} else {
return "Enumeration with underlying type " + type2string(ed->getIntegerType().getCanonicalType(), nullptr);
}
} else if (const clang::RecordType *rct = qual->getAs<clang::RecordType>()) {
clang::RecordDecl *rc = rct->getDecl();
if (!rc) {
return "Unknown record";
} else if (rc->getNameAsString() == "__builtin_va_list") {
return "va_list";
} else {
return "Unknown record " + std::string(rc->getName().data());
}
} else {
return std::string("??? ") + qual->getTypeClassName();
}
}
class Visitor : public clang::RecursiveASTVisitor<Visitor> {
public:
clang::ASTContext &context;
bool shouldVisitTemplateInstantiations() const /* override */ { return true; }
Visitor(clang::CompilerInstance &ci) : context(ci.getASTContext()) {
if (!mangler) {
mangler = clang::ItaniumMangleContext::create(context, ci.getDiagnostics());
}
}
~Visitor() {
if (mangler) {
delete mangler;
mangler = nullptr;
}
}
bool VisitDecl(clang::Decl *decl) /* override */ {
std::cerr << std::flush;
if (!decl) return true;
if ((decl->getKind() >= clang::Decl::Kind::firstFunction) && (decl->getKind() <= clang::Decl::Kind::lastFunction)) {
clang::DeclaratorDecl *ddecl = static_cast<clang::DeclaratorDecl*>(decl);
std::cout << "Function detected!\n";
std::string funName{ddecl->getName()};
auto niceprint = [](const std::string &infotype, const auto &dat){ std::cout << " " << infotype << ": " << dat << "\n"; };
niceprint("Function name", funName);
if (mangler && mangler->shouldMangleDeclName(ddecl)) {
std::string mangled;
{
llvm::raw_string_ostream strstr{mangled};
mangler->mangleName(ddecl, strstr);
}
niceprint("Function mangled name", mangled);
funName = std::move(mangled);
}
bool valid;
std::string funTypeStr{""};
if (ddecl->getFunctionType()->isFunctionNoProtoType()) {
const clang::FunctionNoProtoType *funType = static_cast<const clang::FunctionNoProtoType*>(ddecl->getFunctionType());
const auto &retType = funType->getReturnType();
niceprint("Function return type", type2string(retType, &retType));
niceprint("Canonical function return type",
type2string(retType.getCanonicalType(), &retType) +
" (" + type2char(retType.getCanonicalType(), &retType) + ")");
niceprint("Is sugared", funType->isSugared());
if (funType->isSugared()) {
clang::QualType qft{funType, 0};
niceprint("Desugared", type2string(funType->desugar(), &qft));
}
funTypeStr = type2char(retType.getCanonicalType(), &retType) + std::string("Fv");
valid = isTypeValid(retType.getCanonicalType(), &retType);
} else {
const clang::FunctionProtoType *funType = static_cast<const clang::FunctionProtoType*>(ddecl->getFunctionType());
const auto &retType = funType->getReturnType();
niceprint("Function return type", type2string(retType, &retType));
niceprint("Canonical function return type",
type2string(retType.getCanonicalType(), &retType)
+ " (" + type2char(retType.getCanonicalType(), &retType) + ")");
niceprint("Parameter count", funType->getNumParams());
for (const clang::QualType &type : funType->getParamTypes()) {
niceprint(" " + type2string(type, &type),
type2string(type.getCanonicalType(), &type) + " (" + type2char(type.getCanonicalType(), &type) + ")");
}
niceprint("Variadic function", funType->isVariadic() ? "yes" : "no");
funTypeStr =
type2char(retType.getCanonicalType(), &retType) +
((funType->getNumParams() == 0)
? std::string("Fv") : std::accumulate(funType->getParamTypes().begin(), funType->getParamTypes().end(), std::string("F"),
[](const std::string &acc, const clang::QualType &qual){ return acc + type2char(qual.getCanonicalType(), &qual); }));
if (funType->isVariadic()) funTypeStr += "V";
valid = !funType->isVariadic() &&
std::accumulate(funType->getParamTypes().begin(), funType->getParamTypes().end(), isTypeValid(retType.getCanonicalType(), &retType),
[](bool acc, const clang::QualType &qual){ return acc && isTypeValid(qual.getCanonicalType(), &qual); });
}
niceprint("Conclusion", "");
niceprint("Function final name", funName);
niceprint("Function type", funTypeStr);
niceprint("Valid function type", valid ? "yes" : "no");
std::cout << "\n";
funMap[funName] = std::make_pair(funTypeStr, valid);
funList.push_back(funName);
}
return true;
}
};
class Consumer : public clang::ASTConsumer {
public:
Visitor visitor;
Consumer(clang::CompilerInstance &ci) : visitor(ci) {
}
void HandleTranslationUnit(clang::ASTContext &context) override {
visitor.TraverseDecl(context.getTranslationUnitDecl());
}
};
class Action : public clang::ASTFrontendAction {
public:
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &ci, llvm::StringRef inFile) override {
return std::make_unique<Consumer>(ci);
}
};
int main(int argc, const char **argv) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " (filenames) -- [-I...]" << std::endl;
return 2;
}
/*int fakeargc = argc + 1;
const char **fakeargv = new const char*[fakeargc];
memcpy(fakeargv, argv, argc * sizeof(char*));
fakeargv[fakeargc - 1] = "--";*/
llvm::cl::OptionCategory opcat{""};
clang::tooling::CommonOptionsParser op{argc, argv, opcat};
std::vector<std::string> paths; for (int i = 1; i < argc; ++i) paths.push_back(argv[i]);
clang::tooling::ClangTool tool{op.getCompilations(), paths};
tool.run(clang::tooling::newFrontendActionFactory<Action>().get());
std::cout << "Done, outputing output.h" << std::endl;
std::sort(funList.begin(), funList.end());
std::fstream file{"output.h", std::ios_base::out};
for (const std::string &funName : funList) {
if (!funMap[funName].second) {
file << "//";
}
file << "GO(" << funName << ", " << funMap[funName].first << ")\n";
}
return 0;
}

View File

@ -1,267 +0,0 @@
#include <string>
#define ALL START() \
/* libc */ \
STRUCT("_IO_FILE", "a FILE") \
STRUCT("_G_fpos_t", "a file position") \
STRUCT("sockaddr", "a socket address") \
STRUCT("itimerspec", "an itimerspec") \
STRUCT("timespec", "a timespec") \
STRUCT("itimerval", "an itimerval") \
STRUCT("timeval", "a timeval") \
STRUCT("timex", "a timex") \
STRUCT("timezone", "a timezone") \
STRUCT("dirent", "a dirent") \
STRUCT("dirent64", "a dirent64") \
STRUCT("__dirstream", "a dir stream") \
STRUCT("tm", "a time structure (tm)") \
STRUCT("cmsghdr", "a cmsghdr") \
STRUCT("msghdr", "a msghdr") \
STRUCT("rpcent", "an rpcent") \
STRUCT("random_data", "a random_data structure") \
STRUCT("drand48_data", "a drand48_data structure") \
STRUCT("termios", "a termios") \
STRUCT("iovec", "an iovec") \
STRUCT("file_handle", "a file handle") \
STRUCT("lconv", "an lconv") \
STRUCT("__locale_struct", "a locale structure") \
STRUCT("aliasent", "an alias") \
STRUCT("fstab", "an fstab") \
STRUCT("group", "a group") \
STRUCT("hostent", "a hostent") \
STRUCT("protoent", "a protoent") \
STRUCT("passwd", "a password") \
STRUCT("spwd", "an spwd") \
STRUCT("ttyent", "a ttyent") \
STRUCT("utmp", "an utmp structure") \
STRUCT("utmpx", "an utmpx structure") \
STRUCT("ifaddrs", "an ifaddrs structure") \
STRUCT("statfs", "a statfs structure") \
STRUCT("statfs64", "a statfs64 structure") \
STRUCT("statvfs", "a statvfs structure") \
STRUCT("statvfs64", "a statvfs64 structure") \
STRUCT("timeb", "a timeb structure") \
STRUCT("_ftsent", "an _ftsent structure") \
STRUCT("sysinfo", "a sysinfo structure") \
STRUCT("rlimit", "an rlimit structure") \
STRUCT("rlimit64", "an rlimit64 structure") \
STRUCT("rusage", "an rusage structure") \
STRUCT("entry", "an entry structure") \
STRUCT("pollfd", "a pollfd structure") \
STRUCT("re_pattern_buffer", "a re_pattern_buffer structure") \
STRUCT("sembuf", "a sembuf structure") \
STRUCT("tms", "a tms structure") \
STRUCT("utsname", "an utsname structure") \
STRUCT("utimbuf", "an utimbuf structure") \
STRUCT2("__va_list_tag", "__va_list_tag (aka, a va_list)", 'A') \
/* ncurses */ \
STRUCT("_win_st", "a _win_st structure") \
STRUCT("MEVENT", "an MEVENT structure") \
TYPEDEF("cchar_t", "a cchar_t") \
/* zlib */ \
STRUCT("gz_header_s", "a gz_header_s structure") \
STRUCT("gzFile_s", "a gzFile_s structure") \
STRUCT("z_stream_s", "a z_stream_s structure") \
\
END()
#define START()
#define STRUCT(s, ret) if (str == s) { return 'p'; } else
#define STRUCT2(s, ret, c) if (str == s) { return c; } else
#define TYPEDEF(s, ret) if (str == s) { return 'p'; } else
#define END() { return 0; }
char ptr2char(const std::string &str) {
/*if ((str == "_IO_FILE")
|| (str == "_G_fpos_t")
|| (str == "sockaddr")
|| (str == "itimerspec")
|| (str == "timespec")
|| (str == "itimerval")
|| (str == "timeval")
|| (str == "timex")
|| (str == "timezone")
|| (str == "dirent")
|| (str == "dirent64")
|| (str == "__dirstream")
|| (str == "tm")
|| (str == "cmsghdr")
|| (str == "msghdr")
|| (str == "rpcent")
|| (str == "random_data")
|| (str == "drand48_data")
|| (str == "termios")
|| (str == "iovec")
|| (str == "file_handle")
|| (str == "lconv")
|| (str == "__locale_struct")
|| (str == "aliasent")
|| (str == "fstab")
|| (str == "group")
|| (str == "hostent")
|| (str == "protoent")
|| (str == "passwd")
|| (str == "spwd")
|| (str == "ttyent")
|| (str == "utmp")
|| (str == "utmpx")
|| (str == "ifaddrs")
|| (str == "statfs")
|| (str == "statfs64")
|| (str == "statvfs")
|| (str == "timeb")
|| (str == "_ftsent")
|| (str == "sysinfo")
|| (str == "rlimit")
|| (str == "rlimit64")
|| (str == "rusage")
|| (str == "entry")
|| (str == "pollfd")
|| (str == "re_pattern_buffer")
|| (str == "sembuf")
|| (str == "tms")
|| (str == "utsname")
|| (str == "utimbuf")
// ncurses
|| (str == "_win_st")
|| (str == "cchar_t")
) {
// FILE*, fpos_t*, ...
return 'p';
} else if (str == "__va_list_tag") {
return 'A';
} else {
return 0;
}*/
ALL
}
#undef END
#undef TYPEDEF
#undef STRUCT2
#undef STRUCT
#undef START
#define START()
#define STRUCT(s, ret) if (str == s) { return ret; } else
#define STRUCT2(s, ret, c) if (str == s) { return ret; } else
#define TYPEDEF(s, ret) if (str == s) { return ret; } else
#define END() return "";
const char *ptr2str(const std::string &str) {
/*if (str == "_IO_FILE") {
return "a FILE";
} else if (str == "_G_fpos_t") {
return "a file position";
} else if (str == "sockaddr") {
return "a socket address";
} else if (str == "itimerspec") {
return "an itimerspec";
} else if (str == "timespec") {
return "a timespec";
} else if (str == "itimerval") {
return "an itimerval";
} else if (str == "timeval") {
return "a timeval";
} else if (str == "timex") {
return "a timex";
} else if (str == "timezone") {
return "a timezone";
} else if (str == "dirent") {
return "a dirent";
} else if (str == "dirent64") {
return "a dirent64";
} else if (str == "__dirstream") {
return "a dir stream";
} else if (str == "tm") {
return "a time structure (tm)";
} else if (str == "cmsghdr") {
return "a cmsghdr";
} else if (str == "msghdr") {
return "a msghdr";
} else if (str == "rpcent") {
return "an rpcent";
} else if (str == "random_data") {
return "a random_data structure";
} else if (str == "drand48_data") {
return "a drand48_data structure";
} else if (str == "termios") {
return "a termios";
} else if (str == "iovec") {
return "an iovec";
} else if (str == "file_handle") {
return "a file handle";
} else if (str == "lconv") {
return "an lconv";
} else if (str == "__locale_struct") {
return "a locale structure";
} else if (str == "aliasent") {
return "an alias";
} else if (str == "fstab") {
return "an fstab";
} else if (str == "group") {
return "a group";
} else if (str == "hostent") {
return "a hostent";
} else if (str == "protoent") {
return "a protoent";
} else if (str == "passwd") {
return "a password";
} else if (str == "spwd") {
return "an spwd";
} else if (str == "ttyent") {
return "a ttyent";
} else if (str == "utmp") {
return "an utmp structure";
} else if (str == "utmpx") {
return "an utmpx structure";
} else if (str == "ifaddrs") {
return "an ifaddrs structure";
} else if (str == "statfs") {
return "a statfs structure";
} else if (str == "statfs64") {
return "a statfs64 structure";
} else if (str == "statvfs") {
return "a statvfs structure";
} else if (str == "statvfs64") {
return "a statvfs64 structure";
} else if (str == "timeb") {
return "a timeb structure";
} else if (str == "_ftsent") {
return "an _ftsent structure";
} else if (str == "sysinfo") {
return "a sysinfo structure";
} else if (str == "rlimit") {
return "an rlimit structure";
} else if (str == "rlimit64") {
return "an rlimit64 structure";
} else if (str == "rusage") {
return "an rusage structure";
} else if (str == "entry") {
return "an entry structure";
} else if (str == "pollfd") {
return "a pollfd structure";
} else if (str == "re_pattern_buffer") {
return "a re_pattern_buffer structure";
} else if (str == "sembuf") {
return "a sembuf structure";
} else if (str == "tms") {
return "a tms structure";
} else if (str == "utsname") {
return "an utsname structure";
} else if (str == "utimbuf") {
return "an utimbuf structure";
} else if (str == "__va_list_tag") {
return "__va_list_tag (aka, a va_list)";
// ncurses
} else if (str == "_win_st") {
return "a _win_st structure";
} else if (str == "cchar_t") {
return "a cchar_t";
} else return "";*/
ALL
}
#undef END
#undef TYPEDEF
#undef STRUCT2
#undef STRUCT
#undef START

View File

@ -1,7 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project("helper")
find_package(Clang REQUIRED CONFIG)
add_executable(helper main.cpp gen.cpp)
target_include_directories(helper SYSTEM PUBLIC ${CLANG_INCLUDE_DIRS})
target_link_libraries(helper PUBLIC clang-cpp LLVM)

299
wrapperhelper/Makefile Executable file
View File

@ -0,0 +1,299 @@
all:
.PHONY: all
OPTIM?=2
DEBUG?=1
# FORCE_COLOR: set to non-empty, non 0 to force colorized output
# ECHO: set to non-empty, non 0 to echo commands out
help:
@echo 'Targets:'
@echo ' help (current target)'
@echo ' all (default target)'
@echo ' [EXE] - wrapperhelper'
@printf ' $(foreach obj,$(OBJLIST_wrapperhelper), [OBJ] - $(obj)\n)'
@printf ' $(foreach test,$(TESTS), [TST] - $(test)\n)'
@echo ' clean'
@echo ' distclean'
@echo ''
@echo 'Options:'
@echo ' OPTIM: GCC optimization level (-O is prepended) [default: 2]'
@echo ' DEBUG: set to 0 for release build, set to non-0 for debug build [default: 1]'
@echo ' FORCE_COLOR: set to non-0 to force colorized output'
@echo ' ECHO: set to non-0 to echo out commands executed'
@echo ''
@echo 'Current flags:'
@echo ' CPPFLAGS = $(CPPFLAGS)'
@echo ' CFLAGS = $(CFLAGS)'
# @echo ' CXXFLAGS = $(CXXFLAGS)' unused
@echo ' LDFLAGS = $(LDFLAGS)'
@echo ' LDLIBS = $(LDLIBS)'
@echo ''
@echo 'Sanitizers:'
@echo " address ------------ `[ $(ASAN_ON) -eq 1 ] && printf '\033[92mON\033[m' || printf '\033[91mOFF\033[m'`"
@echo " leak --------------- `[ $(LSAN_ON) -eq 1 ] && printf '\033[92mON\033[m' || printf '\033[91mOFF\033[m'`"
@echo " undefined behavior - `[ $(USAN_ON) -eq 1 ] && printf '\033[92mON\033[m' || printf '\033[91mOFF\033[m'`"
.PHONY: help
ifeq ($(ECHO:0=),)
SILENCER:=@
else
SILENCER:=
endif
ifneq ($(strip $(DEBUG)),0)
CPPFLAGS+= -DDEBUG -D_NRELEASE
CFLAGS+= -g
CXXFLAGS+= -g
LDFLAGS+= -g
OBJDIR?=debug
else
CPPFLAGS+= -DRELEASE -D_NDEBUG
OBJDIR?=release
endif
COMMON_WARNINGS:=-Wfatal-errors -fanalyzer -Wall -Wextra
COMMON_WARNINGS+= -Walloc-zero -Wcast-align=strict -Wcast-qual -Wconversion -Wdate-time
COMMON_WARNINGS+= -Wdisabled-optimization -Wduplicated-branches -Wfloat-equal -Wformat-truncation=2
COMMON_WARNINGS+= -Wimplicit-fallthrough=3 -Wlogical-op -Wmissing-format-attribute -Wmissing-include-dirs
COMMON_WARNINGS+= -Wmissing-noreturn -Wnull-dereference -Wredundant-decls -Wundef -Wunreachable-code -Wshift-overflow=2
COMMON_WARNINGS+= -Wstringop-overflow=4
#COMMON_WARNINGS+= -Wstringop-overflow=4 -Wsuggest-attribute=cold -Wsuggest-attribute=const -Wsuggest-attribute=format
#COMMON_WARNINGS+= -Wsuggest-attribute=malloc -Wsuggest-attribute=noreturn -Wsuggest-attribute=pure
COMMON_WARNINGS+= -Wunknown-pragmas -Wunused-macros -Wwrite-strings
COMMON_WARNINGS+= -Werror=attribute-alias=2 -Werror=duplicated-cond -Werror=format=2 -Werror=format-overflow=2
COMMON_WARNINGS+= -Werror=format-signedness -Werror=pointer-arith
COMMON_WARNINGS+= -Werror=return-type -Werror=shadow -Werror=strict-overflow -Werror=switch-enum
CFLAGS_WARNINGS:=-Werror=implicit-function-declaration -Werror=jump-misses-init -Werror=strict-prototypes
CXXFLAGS_WARNINGS:=-Werror=overloaded-virtual -fdiagnostics-show-template-tree -Wno-analyzer-use-of-uninitialized-value
CPPFLAGS+=
CFLAGS:=$(COMMON_WARNINGS) $(CFLAGS_WARNINGS) $(CFLAGS) -std=gnu18 -O$(OPTIM)
CXXFLAGS:=$(COMMON_WARNINGS) $(CXXFLAGS_WARNINGS) $(CXXFLAGS) -std=c++20 -O$(OPTIM)
LDFLAGS+= -O$(OPTIM)
#CPPFLAGS+= -I/usr/include/SDL2 -D_REENTRANT -pthread
#CFLAGS+= -pthread
#CXXFLAGS+= -pthread
#LDLIBS+= -pthread -lSDL2
ifeq (,$(wildcard $(CURDIR)/sanaddress))
ASAN_ON:=0
else
ASAN_ON:=1
CFLAGS+= -fsanitize=address
CXXFLAGS+= -fsanitize=address
LDFLAGS+= -fsanitize=address
endif
ifeq (,$(wildcard $(CURDIR)/sanleak))
LSAN_ON:=0
else
LSAN_ON:=1
CFLAGS+= -fsanitize=leak
CXXFLAGS+= -fsanitize=leak
LDFLAGS+= -fsanitize=leak
endif
ifeq (,$(wildcard $(CURDIR)/sanundefined))
USAN_ON:=0
else
USAN_ON:=1
CFLAGS+= -fsanitize=undefined
CXXFLAGS+= -fsanitize=undefined
LDFLAGS+= -fsanitize=undefined
endif
# Default
# .SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .m .r .y .l .ym .yl .s .S .mod
# .sym .def .h .info .dvi .tex .texinfo .texi .txinfo .w .ch .web .sh .elc .el
SUFFIXES =
.SUFFIXES:
.SECONDEXPANSION:
ifneq ($(MAKECMDGOALS:distclean=clean),clean)
.: ;
bin obj: ; $(SILENCER)test -d $@ || mkdir $@
# $(eval $(call reproduce_tree,<base>))
define reproduce_tree =
$(1) $(1)/parser: | $$$$(@D) ; $(SILENCER)test -d $$@ || mkdir $$@
endef
$(eval $(call reproduce_tree,obj/$(OBJDIR)))
$(eval $(call reproduce_tree,obj/$(OBJDIR)/tests))
$(eval $(call reproduce_tree,makedir))
$(eval $(call reproduce_tree,makedir/tests))
$(eval $(call reproduce_tree,tests))
endif
# Colors:
# -------
# +--------+-----+
# | 3 | 9 |
# +-+--------+-----+
# |0| | | Black
# |1| | RM | Red
# |2| |[MSG]| Green
# |3|Creating| | Yellow
# |4| | CP | Blue
# |5| | LD | Purple
# |6| C++ | | Cyan
# |7| | | Gray/white
# +-+--------+-----+
# $(call colorize,<br_color>,<br_text>,<text_color>,<text>)
ifdef $(if $(FORCE_COLOR:0=),FORCE_COLOR,MAKE_TERMOUT)
CFLAGS:=$(CFLAGS) -fdiagnostics-color
CXXFLAGS:=$(CFLAGS) -fdiagnostics-color
colorize=@printf "\033[$(1)m[$(2)]\033[m \033[$(3)m$(4)\033[m\n"
else
ifeq ($(SILENCER),)
colorize=
else
colorize=@echo "[$(2)] $(4)"
endif
endif
define newline :=
endef
# $(call remove,<list of file names to remove>)
define remove =
$(call colorize,1;91,RM ,91,Removing $(1))
$(SILENCER)$(RM) -r $(1)
endef
# $(eval $(call add_deptree,<compiler with flags>,<output_filename_noext>,<input_filename_withoutsrc>))
ifeq ($(MAKECMDGOALS),distclean)
add_deptree=
else
define add_deptree =
makedir/$(2).mk: | $$$$(@D)
$(call colorize,95,DEP,33,Creating $(3) dependancies)
$(SILENCER)set -e; $(1) -MM src/$(3) \
| sed 's,\($$(notdir $$(basename $(3)))\)\.o[ :]*,$$(dir obj/$(OBJDIR)/$(3))\1.o: $$@'"\n"'$$(dir obj/$(OBJDIR)/$(3))\1.o $$@: ,g' >$$@
include makedir/$(2).mk
endef
endif
OBJLIST=$(OBJLIST_wrapperhelper) $(foreach test,$(TESTS),$(call test_o,$(test)))
OBJLIST_wrapperhelper:=
TESTS:=
# $(call wrapperhelper_o,<base_dir>,<source_filename>,<output_filename>)
wrapperhelper_o=obj/$(OBJDIR)/$(1)$(3).o
# $(eval $(call compile_wrapperhelper_c,<base_dir>,<source_filename>,<output_filename>))
define compile_wrapperhelper_c =
$$(eval $$(call add_deptree,$$(CC) $$(CPPFLAGS) $$(CFLAGS),$(1)$(3),$(1)$(2).c))
OBJLIST_wrapperhelper+= $(call wrapperhelper_o,$(1),$(2),$(3))
$(call wrapperhelper_o,$(1),$(2),$(3)): src/$(1)$(2).c | $$$$(@D)
$(call colorize,36, C ,92,Compiling $$@)
$(SILENCER)$$(CC) $$(CPPFLAGS) $$(CFLAGS) -c src/$(1)$(2).c -o $$@
endef
# $(eval $(call compile_wrapperhelper_cxx,<base_dir>,<source_filename>,<output_filename>))
define compile_wrapperhelper_cxx =
$$(eval $$(call add_deptree,$$(CXX) $$(CPPFLAGS) $$(CXXFLAGS),$(1)$(3),$(1)$(2).cpp))
OBJLIST_wrapperhelper+= $(call wrapperhelper_o,$(1),$(2),$(3))
$(call wrapperhelper_o,$(1),$(2),$(3)): src/$(1)$(2).cpp | $$$$(@D)
$(call colorize,36,C++,92,Compiling $$@)
$(SILENCER)$$(CXX) $$(CPPFLAGS) $$(CXXFLAGS) -c src/$(1)$(2).cpp -o $$@
endef
# $(eval $(call compile_test_c,<test_dir/name>))
define compile_test_c =
$$(eval $$(call add_deptree,$$(CC) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CFLAGS),tests/$(1),tests/$(1).c))
TESTS+= $(1)
tests/$(1): obj/$(OBJDIR)/tests/$(1).o | $$$$(@D)
$(call colorize,95,LD ,92,Linking $$@)
$(SILENCER)$$(CC) $$(LDFLAGS) -o $$@ obj/$(OBJDIR)/tests/$(1).o $$(LDLIBS)
obj/$(OBJDIR)/tests/$(1).o: src/tests/$(1).c | $$$$(@D)
$(call colorize,36,C++,92,Compiling $$@)
$(SILENCER)$$(CC) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CFLAGS) -c src/tests/$(1).c -o $$@
endef
# $(eval $(call compile_test_cxx,<test_dir/name>))
define compile_test_cxx =
$$(eval $$(call add_deptree,$$(CXX) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CXXFLAGS),tests/$(1),tests/$(1).cpp))
TESTS+= $(1)
tests/$(1): obj/$(OBJDIR)/tests/$(1).o | $$$$(@D)
$(call colorize,95,LD ,92,Linking $$@)
$(SILENCER)$$(CXX) $$(LDFLAGS) -o $$@ obj/$(OBJDIR)/tests/$(1).o $$(LDLIBS)
obj/$(OBJDIR)/tests/$(1).o: src/tests/$(1).cpp | $$$$(@D)
$(call colorize,36,C++,92,Compiling $$@)
$(SILENCER)$$(CXX) $$(CPPFLAGS) -Isrc/tests -Isrc $$(CXXFLAGS) -c src/tests/$(1).cpp -o $$@
endef
$(eval $(call compile_wrapperhelper_c,,cstring,cstring))
$(eval $(call compile_wrapperhelper_c,,generator,generator))
$(eval $(call compile_wrapperhelper_c,,lang,lang))
$(eval $(call compile_wrapperhelper_c,,main,main))
$(eval $(call compile_wrapperhelper_c,,parse,parse))
$(eval $(call compile_wrapperhelper_c,,prepare,prepare))
$(eval $(call compile_wrapperhelper_c,,preproc,preproc))
$(eval $(call compile_wrapperhelper_c,,vector,vector))
$(call wrapperhelper_o,,preproc,preproc): CFLAGS+= -fno-analyzer
$(call wrapperhelper_o,,parse,parse): CFLAGS+= -fno-analyzer
#$(eval $(call compile_test_cxx,core/number))
bin/wrapperhelper: $$(OBJLIST_wrapperhelper) | $$(@D)
$(call colorize,95,LD ,92,Linking $@)
$(SILENCER)$(CXX) $(LDFLAGS) -o $@ $(OBJLIST_wrapperhelper) $(LDLIBS)
wrapperhelper: bin/wrapperhelper
alltests: $(TESTS:%=tests/%)
.PHONY: wrapperhelper alltests
all: wrapperhelper alltests
clean:
$(call remove,$(OBJLIST))
$(call remove,bin/wrapperhelper)
$(call remove,$(TESTS:%=obj/$(OBJDIR)/tests/%.o))
$(call remove,$(TESTS:%=tests/%))
.PHONY: clean
distclean:
$(call remove,makedir)
$(call remove,obj)
$(call remove,bin tests)
.PHONY: distclean
sanitize/help:
@echo "Sanitizers:"
@echo "- address (removes leak)"
@echo "- leak (removes address)"
@echo "- undefined behavior"
@echo ""
@echo "Currently active options:"
@[ $(ASAN_ON) -eq 0 ] || echo "- address"
@[ $(LSAN_ON) -eq 0 ] || echo "- leak"
@[ $(USAN_ON) -eq 0 ] || echo "- undefined behavior"
sanitize/address:
@[ $(ASAN_ON) -eq 0 ] && echo "Not sanitizing address" || echo "Sanitizing address"
sanitize/leak:
@[ $(LSAN_ON) -eq 0 ] && echo "Not sanitizing leak" || echo "Sanitizing leak"
sanitize/undefined:
@[ $(USAN_ON) -eq 0 ] && echo "Not sanitizing undefined behavior" || echo "Sanitizing undefined behavior"
sanitize/address/on:
$(SILENCER)touch sanaddress
$(SILENCER)$(RM) sanleak
sanitize/leak/on:
$(SILENCER)touch sanleak
$(SILENCER)$(RM) sanaddress
sanitize/undefined/on:
$(SILENCER)touch sanundefined
sanitize/address/off:
$(SILENCER)$(RM) sanaddress
sanitize/leak/off:
$(SILENCER)$(RM) sanleak
sanitize/undefined/off:
$(SILENCER)$(RM) sanundefined
.PHONY: sanitize/address sanitize/leak sanitize/undefined
.PHONY: sanitize/address/on sanitize/leak/on sanitize/undefined/on
.PHONY: sanitize/address/off sanitize/leak/off sanitize/undefined/off
tree:
@tree src
.PHONY: tree
.DELETE_ON_ERROR:

View File

@ -1,99 +1,122 @@
# Wrapper helper
**WARNING: There are still many problems with this tool. Please do NOT submit code generated directly by the tool, you should only use it as a preliminary reference.**
This folder is semi-independent from the parent project (`box64`). This sub-project aims to (partially) automating the generation of the private headers in `src/wrapped`. This is, however, still a work-in-progress and in alpha.
As such, **this sub-project is mainly aimed at people who know how to read code and are familiar with the wrapped libraries part of `box64`**.
This tool is based on libclangtooling.
## Licensing
It parses the AST of the library header files, generating the required structures of the wrapping library, including:
- structure definitions,
- export function signatures,
- callback function wrapping,
etc. Of course, this cannot completely automate everything, it can only be used as a reference.
This program is under the MIT license. However, some system header files under the LGPL license (copied from a GNU libc Arch Linux installation) have been adapted into the `include-fixed` folder; these files are not copied into the output and simply serve as data. As such, I believe this falls under fair use, and does not lead to the output of this program (used in the parent `box64` project) being under the (L)GPL license.
At the same time, this tool is also quite rough, and may even have errors.
## Compiling
## Build
You need a C compiler and GNU Make. No library is required.
```
sudo apt install libclang-14-dev
cd wrapperhelper
mkdir build; cd build; cmake ..
make
Go to this folder, then run the `make` command. This will produce a binary called `bin/wrapperhelper`.
This project has been compiled and tested with `GCC 14.2.1 20240805` on an `x86_64` machine, with no warning emitted.
You may also use the `make clean` and `make distclean` commands to remove output files (`clean`) and directories (`distclean`).
## Usage
To use the wrapper helper, run the following command in the folder containing this `README.md`:
```sh
bin/wrapperhelper "path_to_support_file" "path_to_private.h" "path_to_private.h"
```
## Usage:
The first file is a `C` file containing every declaration required. The second file is the "requests" input. The third file is the output file, which may be a different file.
helper <filename> <libname> [guest_triple] [host_triple] -- <clang_flags>
<filename> : set the header file to be parsed
<libname> : set libname required for wrapping func
[guest_triple]: set guest triple: can be arm32/arm64/x86/x64, default is x64
[host_triple] : set host triple: can be arm32/arm64/x86/x64, default is arm64
-- : mandatory
<clang_flags> : extra compiler flags
### Usage example:
`./helper /usr/include/jpeglib.h libjpeg x64 arm64 -- -I /usr/lib/gcc/x86_*/12.2.0/include --include /usr/lib/gcc/x86_*/12.2.0/include/stddef.h --include /usr/include/stdio.h`
You would see an output similar to the files `src/wrapped/wrappedlibjpeg.c` and `src/wrapped/wrappedlibjpeg_private.h`, should they exist.
If there are multiple header files to process, write them into a custom header file as input.
### Output sample
Using the command above, we get the following (trimmed) files:
In `wrappedlibjpeg_private.h`:
The support file may contain pragma declarations of the form
```c
...
GO(jpeg_quality_scaling, iFi)
...
GOM(jpeg_destroy, vFEp)
...
#pragma wrappers explicit_simple TYPE
```
where `TYPE` is a `typedef` to a structure. This marks the structure pointed to by `TYPE` as "simple", which means that functions taking such structures are not required to be `GOM`-like.
In `wrappedlibjpeg.c`:
System headers included (directly or indirectly) by the support file are overriden by the files in `include-fixed`.
The first three lines of the input are ignored.
A "request" is a structure containing an object name and, eventually, a default value (`GO`, `GO2` with type `vFiV` to function `xxx`, `DATA`...) and/or a "solved" value (which is similar, but deduced from the support file).
Valid requests (in the reference file) are:
```c
...
typedef struct jpeg_source_mgr {
void *next_input_byte;
unsigned long bytes_in_buffer;
vFp_t init_source;
iFp_t fill_input_buffer;
vFpI_t skip_input_data;
iFpi_t resync_to_restart;
vFp_t term_source;
} jpeg_source_mgr, *jpeg_source_mgr_ptr;
...
#define GO(A) \
static uintptr_t my_term_source_fct_##A = 0; \
void my_term_source_##A(struct jpeg_decompress_struct * a0) { \
return RunFunction(my_context, my_term_source_fct_##A, 1, a0); \
}
SUPER()
#undef GO
static void* findterm_sourceFct(void* fct) {
if(!fct) return fct;
if(GetNativeFnc((uintptr_t)fct)) return GetNativeFnc((uintptr_t)fct);
#define GO(A) if(my_term_source_fct_##A == (uintptr_t)fct) return my_term_source_##A;}
SUPER()
#undef GO
#define GO(A) if(my_term_source_fct_##A == 0) {my_term_source_fct_##A = (uintptr_t)fct;return my_term_source_##A;}
SUPER()
#undef GO
return NULL;
}
...
EXPORT int my_jpeg_quality_scaling(void *emu, int quality) {
libjpeg_my_t *my = (libjpeg_my_t*)my_lib->priv.w.p2;
my->jpeg_quality_scaling(quality);
}
...
EXPORT void my_jpeg_destroy(void *emu, struct jpeg_common_struct * cinfo) {
// WARN: This function's arg has a structure ptr which is special, may need to wrap it for the host
libjpeg_my_t *my = (libjpeg_my_t*)my_lib->priv.w.p2;
my->jpeg_destroy(cinfo);
}
...
{GO/GOM/GOW/GOWM} ( name , type )
{GOD/GO2/GOWD/GOW2} ( name , type , name )
// {GO/GOM/GOW/GOWM} ( name ,
// {GO/GOM/GOW/GOWM} ( name , type )
// {GOD/GO2/GOWD/GOW2} ( name ,
// {GOD/GO2/GOWD/GOW2} ( name , type , name )
DATA[V/B/M] ( name , int )
// DATA[V/B/M] ( name ,
// DATA[V/B/M] ( name , int )
```
(where `{A/B}` means `A` or `B` and `[A/B]` means `A`, `B` or nothing). All other comments are ignored.
If you want to explore the output of the different stages of the helper, you can use the following forms:
```sh
bin/wrapperhelper --prepare "path_to_support_file" # (1)
bin/wrapperhelper --preproc "path_to_support_file" # (2)
bin/wrapperhelper --proc "path_to_support_file" # (3)
bin/wrapperhelper "path_to_support_file" # (3) as well
```
1. This form outputs the list of preprocessor tokens (the "post-prepare" phase).
2. This form outputs the list of processor tokens (the "post-preprocessor" phase).
3. This form outputs the list of constants, type definitions, structure definitions, and declarations (the "post-processor" phase).
### Example
To remake the `wrappedlibc_private.h` file, use the following command:
```sh
bin/wrapperhelper example-libc.h ../src/wrapped/wrappedlibc_private.h ../src/wrapped/wrappedlibc_private.h
```
This will emit a few marnings and (non-fatal) errors, then write the result directly in `wrappedlibc_private.h`.
## Maintaining
All of the source code is included in the `src` folder.
The `main` function is in `main.c`.
The first phase of compilation (steps 1-3 and a part of step 5 of the translation phases) is implemented in `prepare.c`.
The second phase of compilation (steps 4 and 6) is implemented in `preproc.c`.
The third phase of compilation (step 7) is implemented in `parse.c`, though no actual parsing of function definitions takes place.
The reading and writing of the `_private.h` files is implemented in `generator.c`.
## Known issues
This project only works for `box64`; more work is required for this to be compatible with `box32`.
Only native structures are read. This means that the current version of `wrapperhelper` does not detect an issue when a structure has different members or alignments in two different architectures.
The include paths are hard-coded. There should instead be a structure passed around containing all arch-dependent informations.
Similarly, structure letters (i.e. `S` for `struct _IO_FILE*`) are hard-coded. A pragma should be used instead (`#pragma wrappers type_letter IDENT type-name`, parsed as `PTOK_PRAGMA: Type is letter: <char>`, followed by `type-name`, followed by `PTOK_NEWLINE`).
Conditionals in the `_private.h` files are ignored, except for taking only the negative branch. Manual cleanup of the output is required.
Line numbers are missing entirely. For most errors, finding the corresponding file is difficult (though possible).
Phase 5 is partially implemented, but could be greatly improved.
The following features are missing from the generator:
- Large structures as a parameter
- Large structure as a return type (more than 16 bytes)
- Atomic types
The following features are missing from the preprocessor:
- Error display (`#error` will stop the compilation, but a generic error message will be written)
- General token concatenation (though the concatenation of two `PTOK_IDENT` works without issue)
- Stringify
- Skipped unexpected token warnings
- Proper out-of-memory error handling
The following features are missing from the parser:
- `inline` and `_Noreturn`
- `_Atomic(type-name)`
- `_Alignas(type-name)` and `_Alignas(constant-expression)`
- `(type-name){initializer-list}`
- Function definitions are ignored, not parsed

View File

@ -1,189 +0,0 @@
#pragma once
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/Attr.h>
#include <clang/AST/Decl.h>
#include <clang/AST/Type.h>
#include <clang/Tooling/Tooling.h>
#include <clang/Tooling/CompilationDatabase.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Frontend/FrontendAction.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Tooling/CommonOptionsParser.h>
#include <ios>
#include <llvm/Support/Casting.h>
#include <cstddef>
#include <fstream>
#include <memory>
#include <string>
#include <iostream>
#include <vector>
#include <cstring>
#include <map>
#include "gen.h"
#include "utils.h"
static void ParseParameter(clang::ASTContext* AST, WrapperGenerator* Gen, clang::QualType ParmType, FuncInfo* Func) {
using namespace clang;
(void)AST; (void)Func;
if (ParmType->isFunctionPointerType()) {
auto ProtoType = ParmType->getPointeeType()->getAs<FunctionProtoType>();
for (unsigned i = 0; i < ProtoType->getNumParams(); i++) {
ParseParameter(AST, Gen, ProtoType->getParamType(i), Func);
}
} else if (ParmType->isPointerType()) {
auto PointeeType = ParmType->getPointeeType();
if (PointeeType->isRecordType()) {
if (Gen->records.find(StripTypedef(PointeeType)) == Gen->records.end()) {
auto Record = &Gen->records[StripTypedef(PointeeType)];
if (PointeeType->isUnionType()) {
Record->is_union = true;
}
Record->type = StripTypedef(PointeeType);
Record->decl = PointeeType->getAs<RecordType>()->getDecl();
Record->type_name = Record->decl->getIdentifier() ? Record->decl->getIdentifier()->getName().str() : "<null identifier>";
}
} else if (PointeeType->isPointerType()) {
PointeeType = PointeeType->getPointeeType();
if (PointeeType->isRecordType()) {
if (Gen->records.find(StripTypedef(PointeeType)) == Gen->records.end()) {
auto Record = &Gen->records[StripTypedef(PointeeType)];
if (PointeeType->isUnionType()) {
Record->is_union = true;
}
Record->type = StripTypedef(PointeeType);
Record->decl = PointeeType->getAs<RecordType>()->getDecl();
Record->type_name = Record->decl->getIdentifier() ? Record->decl->getIdentifier()->getName().str() : "<null identifier>";
}
}
}
} else if (ParmType->isRecordType()) {
if (Gen->records.find(StripTypedef(ParmType)) == Gen->records.end()) {
auto Record = &Gen->records[StripTypedef(ParmType)];
if (ParmType->isUnionType()) {
Record->is_union = true;
}
Record->type = StripTypedef(ParmType);
Record->decl = ParmType->getAs<RecordType>()->getDecl();
Record->type_name = Record->decl->getIdentifier() ? Record->decl->getIdentifier()->getName().str() : "<null identifier>";
}
}
}
static void ParseFunction(clang::ASTContext* AST, WrapperGenerator* Gen, clang::FunctionDecl* Decl) {
using namespace clang;
auto Type = Decl->getType().getTypePtr();
auto FuncInfo = &Gen->funcs[Type];
FuncInfo->type = Type;
FuncInfo->func_name = Decl->getNameAsString();
FuncInfo->decl = Decl;
FuncInfo->callback_args.resize(Decl->getNumParams());
if (Decl->getAttr<WeakRefAttr>()) {
FuncInfo->is_weak = true;
}
if (Decl->isVariadic()) {
FuncInfo->is_variadaic = true;
}
for (unsigned i = 0; i < Decl->getNumParams(); i++) {
auto ParmDecl = Decl->getParamDecl(i);
if (ParmDecl->getType()->isFunctionPointerType()) {
FuncInfo->callback_args[i] = ParmDecl->getType().getTypePtr();
FuncInfo->has_callback_arg = true;
} else {
FuncInfo->callback_args[i] = nullptr;
}
ParseParameter(AST, Gen, ParmDecl->getType(), FuncInfo);
}
}
class MyASTVisitor : public clang::RecursiveASTVisitor<MyASTVisitor> {
public:
MyASTVisitor(clang::ASTContext* ctx) : Ctx(ctx) {}
MyASTVisitor(clang::ASTContext* ctx, WrapperGenerator* gen) : Ctx(ctx), Gen(gen) {}
bool VisitFunctionDecl(clang::FunctionDecl* Decl) {
ParseFunction(Ctx, Gen, Decl);
return true;
}
private:
clang::ASTContext* Ctx;
WrapperGenerator* Gen;
};
class MyASTConsumer : public clang::ASTConsumer {
public:
MyASTConsumer(clang::ASTContext* Context, const std::string& libname, const std::string& host_triple, const std::string& guest_triple)
: Visitor(Context, &Generator) {
Generator.Init(libname, host_triple, guest_triple);
}
void HandleTranslationUnit(clang::ASTContext &Ctx) override {
Visitor.TraverseDecl(Ctx.getTranslationUnitDecl());
std::cout << "--------------- Libclangtooling parse complete -----------------\n";
Generator.Prepare(&Ctx);
std::cout << "--------------- Generator prepare complete -----------------\n";
std::ofstream FuncDeclFile("wrapped" + Generator.libname + "_private.h", std::ios::out);
FuncDeclFile << Generator.GenFuncDeclare(&Ctx);
FuncDeclFile.close();
std::ofstream FuncDefineFile("wrapped" + Generator.libname + ".c", std::ios::out);
FuncDefineFile << "#include <stdio.h>\n"
"#include <stdlib.h>\n"
"#include <string.h>\n"
"#define _GNU_SOURCE /* See feature_test_macros(7) */\n"
"#include <dlfcn.h>\n"
"\n"
"#include \"wrappedlibs.h\"\n"
"\n"
"#include \"debug.h\"\n"
"#include \"wrapper.h\"\n"
"#include \"bridge.h\"\n"
"#include \"x64emu.h\"\n"
"#include \"box64context.h\"\n"
"\n"
"const char* " + Generator.libname + "Name = \"" + Generator.libname + "\";\n"
"#define LIBNAME " + Generator.libname + "\n"
"\n"
"#define ADDED_FUNCTIONS() \\\n"
"\n"
"#include \"generated/wrapped" + Generator.libname + "types.h\"\n";
FuncDefineFile << Generator.GenRecordDeclare(&Ctx);
FuncDefineFile << Generator.GenRecordConvert(&Ctx);
FuncDefineFile << Generator.GenCallbackWrap(&Ctx);
FuncDefineFile << Generator.GenFuncDefine(&Ctx);
FuncDefineFile.close();
std::cout << "--------------- Generator gen complete -----------------\n";
}
private:
MyASTVisitor Visitor;
WrapperGenerator Generator;
};
class MyGenAction : public clang::ASTFrontendAction {
public:
MyGenAction(const std::string& libname, const std::string& host_triple, const std::string& guest_triple) :
libname(libname), host_triple(host_triple), guest_triple(guest_triple) {}
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance& Compiler, clang::StringRef file) override {
(void)file;
return std::make_unique<MyASTConsumer>(&Compiler.getASTContext(), libname, host_triple, guest_triple);
}
private:
std::string libname;
std::string host_triple;
std::string guest_triple;
};
class MyFrontendActionFactory : public clang::tooling::FrontendActionFactory {
public:
MyFrontendActionFactory(const std::string& libname, const std::string& host_triple, const std::string& guest_triple) :
libname(libname), host_triple(host_triple), guest_triple(guest_triple) {}
private:
std::unique_ptr<clang::FrontendAction> create() override {
return std::make_unique<MyGenAction>(libname, host_triple, guest_triple);
}
private:
std::string libname;
std::string host_triple;
std::string guest_triple;
};

View File

@ -0,0 +1,170 @@
#define __x86_64__
#define __WCHAR_MAX__ 2147483647
#define __WCHAR_MIN__ (-__WCHAR_MAX - 1)
#define _GNU_SOURCE 1
#define __USE_MISC 1
#define PORTMAP
#define __WORDSIZE 64
// Based on /usr/include/clang/Basic/TokenKinds.def
// Alternate spelling for various tokens. There are GCC extensions in all
// languages, but should not be disabled in strict conformance mode.
#define __alignof__ __alignof
#define __asm asm
#define __asm__ asm
#define __complex _Complex
#define __complex__ _Complex
#define __const const
#define __const__ const
#define __decltype decltype
#define __imag__ __imag
#define __inline inline
#define __inline__ inline
#define __nullptr nullptr
#define __real__ __real
#define __restrict restrict
#define __restrict__ restrict
#define __signed signed
#define __signed__ signed
#define __typeof typeof
#define __typeof__ typeof
#define __volatile volatile
#define __volatile__ volatile
typedef __int128 __int128_t;
typedef unsigned __int128 __uint128_t;
// TODO
#define inline
#include <stdint.h>
#include <aliases.h>
#include <argp.h>
#include <argz.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <asm/prctl.h>
#include <complex.h>
#include <ctype.h>
#include <dirent.h>
#include <envz.h>
#include <err.h>
#include <error.h>
#include <execinfo.h>
#include <event.h>
#include <fcntl.h>
#include <fmtmsg.h>
#include <fnmatch.h>
#include <fstab.h>
#include <fts.h>
#include <ftw.h>
#include <gconv.h>
#include <glob.h>
#include <gnu/libc-version.h>
#include <grp.h>
#include <gshadow.h>
#include <iconv.h>
#include <ifaddrs.h>
#include <inttypes.h>
#include <langinfo.h>
#include <libgen.h>
#include <libintl.h>
#include <link.h>
#include <linux/module.h>
#include <locale.h>
#include <math.h>
#include <malloc.h>
#include <mcheck.h>
#include <mntent.h>
#include <monetary.h>
#include <netdb.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
#include <nl_types.h>
#include <obstack.h>
#include <poll.h>
#include <printf.h>
#include <pwd.h>
#include <regex.h>
#include <resolv.h>
#include <rpc/auth.h>
#include <rpc/des_crypt.h>
#include <rpc/key_prot.h>
#include <rpc/rpc.h>
#include <sched.h>
#include <search.h>
#include <setjmp.h>
#include <shadow.h>
#include <signal.h>
#include <spawn.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/auxv.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/fanotify.h>
#include <sys/file.h>
#include <sys/fsuid.h>
#include <sys/inotify.h>
#include <sys/io.h>
#include <sys/ipc.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/msg.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/profil.h>
#include <sys/ptrace.h>
#include <sys/quota.h>
#include <sys/random.h>
#include <sys/reboot.h>
#include <sys/resource.h>
#include <sys/sem.h>
#include <sys/sendfile.h>
#include <sys/shm.h>
#include <sys/signal.h>
#include <sys/signalfd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/statvfs.h>
#include <sys/swap.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/syslog.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/timerfd.h>
#include <sys/times.h>
#include <sys/timex.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/vfs.h>
#include <sys/xattr.h>
#include <syslog.h>
#include <termios.h>
#include <time.h>
#include <threads.h>
#include <ttyent.h>
#include <uchar.h>
#include <ucontext.h>
#include <ulimit.h>
#include <unistd.h>
#include <unistdio.h>
#include <utime.h>
#include <utmp.h>
#include <utmpx.h>
#include <wait.h>
#include <wchar.h>
#include <wctype.h>
#include <wordexp.h>
#pragma wrappers explicit_simple FTS
#pragma wrappers explicit_simple FTS64
#pragma wrappers explicit_simple glob_t
#pragma wrappers explicit_simple glob64_t

File diff suppressed because it is too large Load Diff

View File

@ -1,144 +0,0 @@
#pragma once
#include <clang/AST/ASTContext.h>
#include <clang/AST/Type.h>
#include <cstdint>
#include <string>
#include <vector>
#include <set>
struct FuncDefinition {
std::vector<const clang::Type*> arg_types;
std::vector<std::string> arg_types_str;
std::vector<std::string> arg_names;
const clang::Type* ret;
std::string ret_str;
int arg_size;
bool is_variadaic;
};
struct FuncInfo {
const clang::Type* type;
clang::FunctionDecl* decl;
std::string func_name;
bool is_weak;
bool is_variadaic;
bool has_special_arg;
bool has_special_ret;
bool has_callback_arg;
std::vector<const clang::Type*> callback_args;
};
struct RecordInfo {
const clang::Type* type;
clang::RecordDecl* decl;
std::string type_name;
bool is_union;
bool is_special;
uint64_t guest_size;
uint64_t host_size;
std::vector<const clang::Type*> callback_fields;
};
struct ObjectInfo {
const clang::Type* type;
std::string object_name;
};
struct WrapperGenerator {
void Init(const std::string& libname, const std::string& host_triple, const std::string& guest_triple) {
this->host_triple = host_triple;
this->guest_triple = guest_triple;
this->libname = libname;
this->my_lib_type = libname + "_my_t";
this->my_lib = "my_lib";
}
void Prepare(clang::ASTContext* Ctx);
std::string GenCallbackTypeDefs(clang::ASTContext* Ctx);
std::string GenFuncDeclare(clang::ASTContext* Ctx) {
std::string res{};
for (const auto& func : funcs) {
res += GenDeclare(Ctx, func.second);
}
return res;
}
std::string GenRecordDeclare(clang::ASTContext* Ctx) {
std::string res{};
for (const auto& st : records) {
if (st.second.host_size == st.second.guest_size)
res += GenDeclare(Ctx, st.second);
else {
res += GenDeclareDiffTriple(Ctx, st.second, guest_triple, host_triple);
}
}
return res;
}
std::string GenFuncDefine(clang::ASTContext* Ctx) {
std::string res{};
for (const auto& func : funcs) {
res += GenDefine(Ctx, func.second);
}
return res;
}
std::string GenCallbackWrap(clang::ASTContext* Ctx) {
std::string res{};
for (const auto& func : funcs) {
res += GenCallbackWrap(Ctx, func.second);
}
for (const auto& st : records) {
res += GenCallbackWrap(Ctx, st.second);
}
return res;
}
std::string GenRecordConvert(clang::ASTContext* Ctx) {
(void)Ctx;
std::string res;
for (const auto& record : records) {
if (record.second.host_size != record.second.guest_size) {
res += GenRecordConvert(record.second);
}
}
return res;
}
std::map<const clang::Type*, FuncInfo> funcs;
std::map<const clang::Type*, RecordInfo> records;
std::map<const clang::Type*, ObjectInfo> objects;
std::map<const clang::Type*, std::string> callbacks;
std::string host_triple;
std::string guest_triple;
std::string libname;
std::string my_lib_type;
std::string my_lib;
private:
std::string GenRecordConvert(const RecordInfo& Record);
std::string GenDeclare(clang::ASTContext* Ctx, const FuncInfo& Func);
std::string GenDefine(clang::ASTContext* Ctx, const FuncInfo& Func);
std::string GenCallbackWrap(clang::ASTContext* Ctx, const FuncInfo& Func);
std::string GenDeclareDiffTriple(clang::ASTContext* Ctx, const RecordInfo& Record, const std::string& GuestTriple, const std::string& HostTriple);
std::string GenDeclare(clang::ASTContext* Ctx, const RecordInfo& Struct);
std::string GenCallbackWrap(clang::ASTContext* Ctx, const RecordInfo& Struct);
void ParseRecordRecursive(clang::ASTContext* Ctx, const clang::Type* Type, bool& Special, std::set<const clang::Type*>& Visited);
std::string TypeStringify(const clang::Type* Type, clang::FieldDecl* FieldDecl, clang::ParmVarDecl* ParmDecl, std::string& PreDecl, std::string indent = "", std::string Name = "");
std::string SimpleTypeStringify(const clang::Type* Type, clang::FieldDecl* FieldDecl, clang::ParmVarDecl* ParmDecl, std::string indent = "", std::string Name = "");
std::string AnonRecordDecl(const clang::RecordType* Type, std::string& PreDecl, std::string indent);
std::string SimpleAnonRecordDecl(const clang::RecordType* Type, std::string indent);
FuncDefinition GetFuncDefinition(const clang::Type* Type);
FuncDefinition GetFuncDefinition(clang::FunctionDecl* Decl);
uint64_t GetRecordSize(const clang::Type* Type, const std::string& Triple);
std::vector<uint64_t> GetRecordFieldOffDiff(const clang::Type* Type, const std::string& GuestTriple, const std::string& HostTriple, std::vector<uint64_t>& GuestFieldOff, std::vector<uint64_t>& HostFieldOff);
clang::CharUnits::QuantityType GetRecordAlign(const clang::Type* Type, const std::string& Triple);
std::string GetFuncSig(clang::ASTContext* CTX, const FuncInfo& Decl);
std::string GetFuncSig(clang::ASTContext* CTX, const clang::Type* Type);
};

View File

@ -0,0 +1,32 @@
/* Define intN_t types.
Copyright (C) 2017-2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.
This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024.
*/
#ifndef _BITS_STDINT_INTN_H
#define _BITS_STDINT_INTN_H 1
#include <bits/types.h>
typedef __int8_t int8_t;
typedef __int16_t int16_t;
typedef __int32_t int32_t;
typedef __int64_t int64_t;
#endif /* bits/stdint-intn.h */

View File

@ -0,0 +1,39 @@
/* Define int_leastN_t and uint_leastN types.
Copyright (C) 2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.
This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024.
*/
#ifndef _BITS_STDINT_LEAST_H
#define _BITS_STDINT_LEAST_H 1
#include <bits/types.h>
/* Signed. */
typedef __int_least8_t int_least8_t;
typedef __int_least16_t int_least16_t;
typedef __int_least32_t int_least32_t;
typedef __int_least64_t int_least64_t;
/* Unsigned. */
typedef __uint_least8_t uint_least8_t;
typedef __uint_least16_t uint_least16_t;
typedef __uint_least32_t uint_least32_t;
typedef __uint_least64_t uint_least64_t;
#endif /* bits/stdint-least.h */

View File

@ -0,0 +1,32 @@
/* Define uintN_t types.
Copyright (C) 2017-2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.
This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024.
*/
#ifndef _BITS_STDINT_UINTN_H
#define _BITS_STDINT_UINTN_H 1
#include <bits/types.h>
typedef __uint8_t uint8_t;
typedef __uint16_t uint16_t;
typedef __uint32_t uint32_t;
typedef __uint64_t uint64_t;
#endif /* bits/stdint-uintn.h */

View File

@ -0,0 +1,192 @@
/* bits/types.h -- definitions of __*_t types underlying *_t types.
Copyright (C) 2002-2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.
This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024.
*/
/*
* Never include this file directly; use <sys/types.h> instead.
*/
#ifndef _BITS_TYPES_H
#define _BITS_TYPES_H 1
#include <features.h>
#include <bits/wordsize.h>
#include <bits/timesize.h>
/* Convenience types. */
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;
/* Fixed-size types, underlying types depend on word size and compiler. */
#pragma wrappers allow_ints_ext
/* Smallest types with at least a given width. */
typedef __int8_t __int_least8_t;
typedef __uint8_t __uint_least8_t;
typedef __int16_t __int_least16_t;
typedef __uint16_t __uint_least16_t;
typedef __int32_t __int_least32_t;
typedef __uint32_t __uint_least32_t;
typedef __int64_t __int_least64_t;
typedef __uint64_t __uint_least64_t;
/* quad_t is also 64 bits. */
typedef __int64_t __quad_t;
typedef __uint64_t __u_quad_t;
/* Largest integral types. */
typedef __int64_t __intmax_t;
typedef __uint64_t __uintmax_t;
/* The machine-dependent file <bits/typesizes.h> defines __*_T_TYPE
macros for each of the OS types we define below. The definitions
of those macros must use the following macros for underlying types.
We define __S<SIZE>_TYPE and __U<SIZE>_TYPE for the signed and unsigned
variants of each of the following integer types on this machine.
16 -- "natural" 16-bit type (always short)
32 -- "natural" 32-bit type (always int)
64 -- "natural" 64-bit type (long or long long)
LONG32 -- 32-bit type, traditionally long
QUAD -- 64-bit type, traditionally long long
WORD -- natural type of __WORDSIZE bits (int or long)
LONGWORD -- type of __WORDSIZE bits, traditionally long
We distinguish WORD/LONGWORD, 32/LONG32, and 64/QUAD so that the
conventional uses of `long' or `long long' type modifiers match the
types we define, even when a less-adorned type would be the same size.
This matters for (somewhat) portably writing printf/scanf formats for
these types, where using the appropriate l or ll format modifiers can
make the typedefs and the formats match up across all GNU platforms. If
we used `long' when it's 64 bits where `long long' is expected, then the
compiler would warn about the formats not matching the argument types,
and the programmer changing them to shut up the compiler would break the
program's portability.
Here we assume what is presently the case in all the GCC configurations
we support: long long is always 64 bits, long is always word/address size,
and int is always 32 bits. */
#define __S16_TYPE __int16_t
#define __U16_TYPE __uint16_t
#define __S32_TYPE __int32_t
#define __U32_TYPE __uint32_t
#define __SLONGWORD_TYPE long int
#define __ULONGWORD_TYPE unsigned long int
# define __SQUAD_TYPE __int64_t
# define __UQUAD_TYPE __uint64_t
# define __SWORD_TYPE long int
# define __UWORD_TYPE unsigned long int
# define __SLONG32_TYPE __int32_t
# define __ULONG32_TYPE __uint32_t
# define __S64_TYPE __int64_t
# define __U64_TYPE __uint64_t
# define __STD_TYPE typedef
#include <bits/typesizes.h> /* Defines __*_T_TYPE macros. */
#include <bits/time64.h> /* Defines __TIME*_T_TYPE macros. */
__STD_TYPE __DEV_T_TYPE __dev_t; /* Type of device numbers. */
__STD_TYPE __UID_T_TYPE __uid_t; /* Type of user identifications. */
__STD_TYPE __GID_T_TYPE __gid_t; /* Type of group identifications. */
__STD_TYPE __INO_T_TYPE __ino_t; /* Type of file serial numbers. */
__STD_TYPE __INO64_T_TYPE __ino64_t; /* Type of file serial numbers (LFS).*/
__STD_TYPE __MODE_T_TYPE __mode_t; /* Type of file attribute bitmasks. */
__STD_TYPE __NLINK_T_TYPE __nlink_t; /* Type of file link counts. */
__STD_TYPE __OFF_T_TYPE __off_t; /* Type of file sizes and offsets. */
__STD_TYPE __OFF64_T_TYPE __off64_t; /* Type of file sizes and offsets (LFS). */
__STD_TYPE __PID_T_TYPE __pid_t; /* Type of process identifications. */
__STD_TYPE __FSID_T_TYPE __fsid_t; /* Type of file system IDs. */
__STD_TYPE __CLOCK_T_TYPE __clock_t; /* Type of CPU usage counts. */
__STD_TYPE __RLIM_T_TYPE __rlim_t; /* Type for resource measurement. */
__STD_TYPE __RLIM64_T_TYPE __rlim64_t; /* Type for resource measurement (LFS). */
__STD_TYPE __ID_T_TYPE __id_t; /* General type for IDs. */
__STD_TYPE __TIME_T_TYPE __time_t; /* Seconds since the Epoch. */
__STD_TYPE __USECONDS_T_TYPE __useconds_t; /* Count of microseconds. */
__STD_TYPE __SUSECONDS_T_TYPE __suseconds_t; /* Signed count of microseconds. */
__STD_TYPE __SUSECONDS64_T_TYPE __suseconds64_t;
__STD_TYPE __DADDR_T_TYPE __daddr_t; /* The type of a disk address. */
__STD_TYPE __KEY_T_TYPE __key_t; /* Type of an IPC key. */
/* Clock ID used in clock and timer functions. */
__STD_TYPE __CLOCKID_T_TYPE __clockid_t;
/* Timer ID returned by `timer_create'. */
__STD_TYPE __TIMER_T_TYPE __timer_t;
/* Type to represent block size. */
__STD_TYPE __BLKSIZE_T_TYPE __blksize_t;
/* Types from the Large File Support interface. */
/* Type to count number of disk blocks. */
__STD_TYPE __BLKCNT_T_TYPE __blkcnt_t;
__STD_TYPE __BLKCNT64_T_TYPE __blkcnt64_t;
/* Type to count file system blocks. */
__STD_TYPE __FSBLKCNT_T_TYPE __fsblkcnt_t;
__STD_TYPE __FSBLKCNT64_T_TYPE __fsblkcnt64_t;
/* Type to count file system nodes. */
__STD_TYPE __FSFILCNT_T_TYPE __fsfilcnt_t;
__STD_TYPE __FSFILCNT64_T_TYPE __fsfilcnt64_t;
/* Type of miscellaneous file system fields. */
__STD_TYPE __FSWORD_T_TYPE __fsword_t;
__STD_TYPE __SSIZE_T_TYPE __ssize_t; /* Type of a byte count, or error. */
/* Signed long type used in system calls. */
__STD_TYPE __SYSCALL_SLONG_TYPE __syscall_slong_t;
/* Unsigned long type used in system calls. */
__STD_TYPE __SYSCALL_ULONG_TYPE __syscall_ulong_t;
/* These few don't really vary by system, they always correspond
to one of the other defined types. */
typedef __off64_t __loff_t; /* Type of file sizes and offsets (LFS). */
typedef char *__caddr_t;
/* Duplicates info from stdint.h but this is used in unistd.h. */
__STD_TYPE __SWORD_TYPE __intptr_t;
/* Duplicate info from sys/socket.h. */
__STD_TYPE __U32_TYPE __socklen_t;
/* C99: An integer type that can be accessed as an atomic entity,
even in the presence of asynchronous interrupts.
It is not currently necessary for this to be machine-specific. */
typedef int __sig_atomic_t;
/* Seconds since the Epoch, visible to user code when time_t is too
narrow only for consistency with the old way of widening too-narrow
types. User code should never use __time64_t. */
#if __TIMESIZE == 64 && defined __LIBC
# define __time64_t __time_t
#elif __TIMESIZE != 64
__STD_TYPE __TIME64_T_TYPE __time64_t;
#endif
#undef __STD_TYPE
#endif /* bits/types.h */

View File

@ -0,0 +1,276 @@
/* Copyright (C) 1997-2024 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>.
This file has been adapted to work with the 'wrapperhelper' project on the 09/06/2024.
*/
/*
* ISO C99: 7.18 Integer types <stdint.h>
*/
#ifndef _STDINT_H
#define _STDINT_H 1
#define __GLIBC_INTERNAL_STARTING_HEADER_IMPLEMENTATION
#include <bits/libc-header-start.h>
#include <bits/types.h>
#include <bits/wchar.h>
#include <bits/wordsize.h>
#pragma wrappers allow_ints_ext
/* Exact integral types. */
/* Signed. */
#include <bits/stdint-intn.h>
/* Unsigned. */
#include <bits/stdint-uintn.h>
/* Small types. */
#include <bits/stdint-least.h>
/* Fast types. */
/* Signed. */
typedef int8_t int_fast8_t;
typedef int16_t int_fast16_t;
typedef int32_t int_fast32_t;
typedef int64_t int_fast64_t;
/* Unsigned. */
typedef uint8_t uint_fast8_t;
typedef uint16_t uint_fast16_t;
typedef uint32_t uint_fast32_t;
typedef uint64_t uint_fast64_t;
/* Types for `void *' pointers. */
# ifndef __intptr_t_defined
typedef signed long intptr_t;
# define __intptr_t_defined
# endif
typedef unsigned long uintptr_t;
/* Largest integral types. */
typedef int64_t intmax_t;
typedef uint64_t uintmax_t;
# if __WORDSIZE == 64
# define __INT64_C(c) c ## L
# define __UINT64_C(c) c ## UL
# else
# define __INT64_C(c) c ## LL
# define __UINT64_C(c) c ## ULL
# endif
/* Limits of integral types. */
/* Minimum of signed integral types. */
# define INT8_MIN (-128)
# define INT16_MIN (-32767-1)
# define INT32_MIN (-2147483647-1)
# define INT64_MIN (-__INT64_C(9223372036854775807)-1)
/* Maximum of signed integral types. */
# define INT8_MAX (127)
# define INT16_MAX (32767)
# define INT32_MAX (2147483647)
# define INT64_MAX (__INT64_C(9223372036854775807))
/* Maximum of unsigned integral types. */
# define UINT8_MAX (255)
# define UINT16_MAX (65535)
# define UINT32_MAX (4294967295U)
# define UINT64_MAX (__UINT64_C(18446744073709551615))
/* Minimum of signed integral types having a minimum size. */
# define INT_LEAST8_MIN (-128)
# define INT_LEAST16_MIN (-32767-1)
# define INT_LEAST32_MIN (-2147483647-1)
# define INT_LEAST64_MIN (-__INT64_C(9223372036854775807)-1)
/* Maximum of signed integral types having a minimum size. */
# define INT_LEAST8_MAX (127)
# define INT_LEAST16_MAX (32767)
# define INT_LEAST32_MAX (2147483647)
# define INT_LEAST64_MAX (__INT64_C(9223372036854775807))
/* Maximum of unsigned integral types having a minimum size. */
# define UINT_LEAST8_MAX (255)
# define UINT_LEAST16_MAX (65535)
# define UINT_LEAST32_MAX (4294967295U)
# define UINT_LEAST64_MAX (__UINT64_C(18446744073709551615))
/* Minimum of fast signed integral types having a minimum size. */
# define INT_FAST8_MIN (-128)
# define INT_FAST16_MIN INT16_MIN
# define INT_FAST32_MIN INT32_MIN
# define INT_FAST64_MIN (-__INT64_C(9223372036854775807)-1)
/* Maximum of fast signed integral types having a minimum size. */
# define INT_FAST8_MAX (127)
# define INT_FAST16_MAX INT16_MAX
# define INT_FAST32_MAX INT32_MAX
# define INT_FAST64_MAX (__INT64_C(9223372036854775807))
/* Maximum of fast unsigned integral types having a minimum size. */
# define UINT_FAST8_MAX (255)
# define UINT_FAST16_MAX UINT16_MAX
# define UINT_FAST32_MAX UINT32_MAX
# define UINT_FAST64_MAX (__UINT64_C(18446744073709551615))
/* Values to test for integral types holding `void *' pointer. */
# if __WORDSIZE == 64
# define INTPTR_MIN (-9223372036854775807L-1)
# define INTPTR_MAX (9223372036854775807L)
# define UINTPTR_MAX (18446744073709551615UL)
# else
# define INTPTR_MIN (-2147483647-1)
# define INTPTR_MAX (2147483647)
# define UINTPTR_MAX (4294967295U)
# endif
/* Minimum for largest signed integral type. */
# define INTMAX_MIN (-__INT64_C(9223372036854775807)-1)
/* Maximum for largest signed integral type. */
# define INTMAX_MAX (__INT64_C(9223372036854775807))
/* Maximum for largest unsigned integral type. */
# define UINTMAX_MAX (__UINT64_C(18446744073709551615))
/* Limits of other integer types. */
/* Limits of `ptrdiff_t' type. */
# if __WORDSIZE == 64
# define PTRDIFF_MIN (-9223372036854775807L-1)
# define PTRDIFF_MAX (9223372036854775807L)
# else
# if __WORDSIZE32_PTRDIFF_LONG
# define PTRDIFF_MIN (-2147483647L-1)
# define PTRDIFF_MAX (2147483647L)
# else
# define PTRDIFF_MIN (-2147483647-1)
# define PTRDIFF_MAX (2147483647)
# endif
# endif
/* Limits of `sig_atomic_t'. */
# define SIG_ATOMIC_MIN (-2147483647-1)
# define SIG_ATOMIC_MAX (2147483647)
/* Limit of `size_t' type. */
# if __WORDSIZE == 64
# define SIZE_MAX (18446744073709551615UL)
# else
# if __WORDSIZE32_SIZE_ULONG
# define SIZE_MAX (4294967295UL)
# else
# define SIZE_MAX (4294967295U)
# endif
# endif
/* Limits of `wchar_t'. */
# ifndef WCHAR_MIN
/* These constants might also be defined in <wchar.h>. */
# define WCHAR_MIN __WCHAR_MIN
# define WCHAR_MAX __WCHAR_MAX
# endif
/* Limits of `wint_t'. */
# define WINT_MIN (0u)
# define WINT_MAX (4294967295u)
/* Signed. */
# define INT8_C(c) c
# define INT16_C(c) c
# define INT32_C(c) c
# if __WORDSIZE == 64
# define INT64_C(c) c ## L
# else
# define INT64_C(c) c ## LL
# endif
/* Unsigned. */
# define UINT8_C(c) c
# define UINT16_C(c) c
# define UINT32_C(c) c ## U
# if __WORDSIZE == 64
# define UINT64_C(c) c ## UL
# else
# define UINT64_C(c) c ## ULL
# endif
/* Maximal type. */
# if __WORDSIZE == 64
# define INTMAX_C(c) c ## L
# define UINTMAX_C(c) c ## UL
# else
# define INTMAX_C(c) c ## LL
# define UINTMAX_C(c) c ## ULL
# endif
#if __GLIBC_USE (IEC_60559_BFP_EXT_C23)
# define INT8_WIDTH 8
# define UINT8_WIDTH 8
# define INT16_WIDTH 16
# define UINT16_WIDTH 16
# define INT32_WIDTH 32
# define UINT32_WIDTH 32
# define INT64_WIDTH 64
# define UINT64_WIDTH 64
# define INT_LEAST8_WIDTH 8
# define UINT_LEAST8_WIDTH 8
# define INT_LEAST16_WIDTH 16
# define UINT_LEAST16_WIDTH 16
# define INT_LEAST32_WIDTH 32
# define UINT_LEAST32_WIDTH 32
# define INT_LEAST64_WIDTH 64
# define UINT_LEAST64_WIDTH 64
# define INT_FAST8_WIDTH 8
# define UINT_FAST8_WIDTH 8
# define INT_FAST16_WIDTH __WORDSIZE
# define UINT_FAST16_WIDTH __WORDSIZE
# define INT_FAST32_WIDTH __WORDSIZE
# define UINT_FAST32_WIDTH __WORDSIZE
# define INT_FAST64_WIDTH 64
# define UINT_FAST64_WIDTH 64
# define INTPTR_WIDTH __WORDSIZE
# define UINTPTR_WIDTH __WORDSIZE
# define INTMAX_WIDTH 64
# define UINTMAX_WIDTH 64
# define PTRDIFF_WIDTH __WORDSIZE
# define SIG_ATOMIC_WIDTH 32
# define SIZE_WIDTH __WORDSIZE
# define WCHAR_WIDTH 32
# define WINT_WIDTH 32
#endif
#endif /* stdint.h */

View File

@ -1,69 +0,0 @@
#include "ast.h"
#include "utils.h"
static void dump_usage() {
std::string Usage = R"usage(
usage: command <filename> <libname> [guest_triple] [host_triple] -- <clang_flags>
<filename> : set the header file to be parsed
<libname> : set libname required for wrapping func
[guest_triple]: set guest triple: can be arm32/arm64/x86/x64, default is x64
[host_triple] : set host triple: can be arm32/arm64/x86/x64, default is arm64
-- : mandatory
<clang_flags> : extra compiler flags
)usage";
std::cerr << Usage << std::endl;
}
std::string parse_triple(const char* arg) {
if (strcmp(arg, "x86") == 0) {
return "i386-pc-linux-gnu";
} else if (strcmp(arg, "x64") == 0) {
return "x86_64-pc-linux-gnu";
} else if (strcmp(arg, "arm32") == 0) {
return "armv7-unknown-linux-gnueabihf";
} else if (strcmp(arg, "arm64") == 0) {
return "aarch64-unknown-linux-gnu";
} else {
std::cerr << "Invalid triple: '" << arg << "'\n";
dump_usage();
return "";
}
}
int main(int argc, const char* argv[]) {
if (argc < 4) {
dump_usage();
return 0;
}
std::string libname = argv[2];
std::string guest_triple = parse_triple("x64");
std::string host_triple = parse_triple("arm64");
if (argc >= 5) {
guest_triple = parse_triple(argv[3]);
}
if (argc >= 6) {
host_triple = parse_triple(argv[4]);
}
bool has_necessary_tag = false;
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "--") == 0) {
has_necessary_tag = true;
break;
}
}
if (!has_necessary_tag) {
std::cerr << "Please add '--' after the triples" << std::endl;
dump_usage();
return 0;
}
std::string err;
auto compile_db = clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv, err);
clang::tooling::ClangTool Tool(*compile_db, {argv[1]});
Tool.appendArgumentsAdjuster([&guest_triple](const clang::tooling::CommandLineArguments &args, clang::StringRef) {
clang::tooling::CommandLineArguments adjusted_args = args;
adjusted_args.push_back(std::string{"-target"});
adjusted_args.push_back(guest_triple);
return adjusted_args;
});
return Tool.run(std::make_unique<MyFrontendActionFactory>(libname, host_triple, guest_triple).get());
}

115
wrapperhelper/src/cstring.c Normal file
View File

@ -0,0 +1,115 @@
#include "cstring.h"
#include <string.h>
#define STRING_MIN_CAP 8
string_t *string_new(void) {
string_t *ret = malloc(sizeof(*ret));
if (!ret) return NULL;
char *buf = malloc(sizeof(char));
if (!buf) {
free(ret);
return NULL;
}
buf[0] = '\0';
ret->ssize = ret->scap = 0; ret->buf = buf;
return ret;
}
string_t *string_new_cap(size_t cap) {
string_t *ret = malloc(sizeof(*ret));
if (!ret) return NULL;
cap = (cap < STRING_MIN_CAP) ? STRING_MIN_CAP : cap;
ret->buf = malloc((cap + 1) * sizeof(char));
if (!ret->buf) {
free(ret);
return NULL;
}
ret->buf[0] = '\0';
ret->scap = cap;
ret->ssize = 0;
return ret;
}
int string_reserve(string_t *s, size_t cap) {
size_t new_cap = (cap < STRING_MIN_CAP) ? STRING_MIN_CAP : cap;
if (new_cap <= s->scap) return 1;
void *new_buf = realloc(s->buf, sizeof(char) * (new_cap + 1));
if (!new_buf) return 0;
s->buf = new_buf;
s->scap = new_cap;
return 1;
}
void string_del(string_t *s) {
if (s->buf) free(s->buf);
free(s);
}
char *string_steal(string_t *s) {
char *ret = s->buf;
free(s);
return ret;
}
int string_add_char(string_t *s, char elem) {
if (s->ssize >= s->scap) {
size_t new_cap = (s->scap < STRING_MIN_CAP) ? STRING_MIN_CAP : s->scap * 2;
char *new_buf = realloc(s->buf, sizeof(char) * (new_cap + 1));
if (!new_buf) return 0;
s->buf = new_buf;
s->scap = new_cap;
}
s->buf[s->ssize++] = elem;
s->buf[s->ssize] = '\0';
return 1;
}
int string_add_string(string_t *s1, string_t *s2) {
if (s1->ssize + s2->ssize > s1->scap) {
size_t new_cap = (s1->scap < STRING_MIN_CAP) ? STRING_MIN_CAP : s1->scap * 2;
while (s1->ssize + s2->ssize > new_cap) {
new_cap = new_cap * 2;
}
char *new_buf = realloc(s1->buf, sizeof(char) * (new_cap + 1));
if (!new_buf) return 0;
s1->buf = new_buf;
s1->scap = new_cap;
}
memcpy(s1->buf + s1->ssize, s2->buf, s2->ssize);
s1->ssize += s2->ssize;
s1->buf[s1->ssize] = '\0';
return 1;
}
void string_pop(string_t *s) {
if (!s->ssize) return;
s->buf[--s->ssize] = '\0';
if (s->ssize < s->scap / 4) {
size_t new_cap = (s->scap / 2 < STRING_MIN_CAP) ? STRING_MIN_CAP : s->scap / 2;
if (new_cap == s->scap) return;
void *new_buf = realloc(s->buf, sizeof(char) * (new_cap + 1));
if (!new_buf) return; // We don't really care if the realloc fails, we just need to not update anything
s->buf = new_buf;
s->scap = new_cap;
}
}
string_t *string_dup(string_t const *s) {
string_t *ret = string_new_cap(s->ssize);
if (!ret) return NULL;
memcpy(ret->buf, s->buf, s->ssize + 1);
ret->ssize = s->ssize;
return ret;
}
string_t *string_concat(string_t const *l, string_t const *r) {
string_t *ret = string_new_cap(l->ssize + r->ssize);
if (!ret) return NULL;
memcpy(ret->buf, l->buf, l->ssize);
memcpy(ret->buf + l->ssize, r->buf, r->ssize);
ret->buf[ret->ssize] = '\0';
return ret;
}

View File

@ -0,0 +1,78 @@
#pragma once
#ifndef STRING_H
#define STRING_H
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
/** Thread-unsafe string implementation, with support for raw NULL bytes
* USAGE:
* ======
* string_t ------------ The string type.
* string_new ---------- Creates a new string.
* string_new_cap ------ Creates a new string with a given capacity. Takes the capacity.
* string_reserve ------ Ensures a string has at least a given capacity. Takes the string and the capacity.
* string_del ---------- Frees a string. Takes the string.
* string_steal -------- Frees a string, keeping the content alive. Takes the string. The content (also returned) needs to be freed separately.
* string_add_char ----- Add a character at the end. Takes the string and the new character. May increase the string capacity.
* string_add_string --- Add a string at the end in-place. Takes both strings. May increase the string capacity.
* string_pop ---------- Pops the last character. Takes the string. May reduce the string capacity.
* string_dup ---------- Duplicate a string. Takes the string. Does not free the old string.
* string_concat ------- Concatenate two strings. Takes both strings. Does not free any string.
* string_len ---------- String length. Takes the string.
* string_cap ---------- String capacity. Takes the string.
* string_content ------ Pointer to the string content. Valid C string. Takes the string.
* string_begin -------- Start of the string. Takes the string.
* string_end ---------- End of the string. Points to unmanaged memory. Takes the string.
* string_last --------- Last element of the string. Points to invalid memory if size is zero. Takes the string.
* string_for ---------- Iterate over the characters of a string. This is a for loop. Takes the iterator name and the string.
*
* EXAMPLE:
* ========
* Source main.c:
* -------------------
// ...
int main() {
string_t *str = string_new_cap(2);
if (!str) {
printf("Error: failed to allocate new string\n");
return 2;
}
string_add_char(str, 'H'); // Cannot fail
string_add_char(str, 'i'); // Cannot fail
if (!string_add_char(str, '!')) {
printf("Error: failed to add char to string\n");
return 2;
}
printf("String length: %zu: \"%s\"\n", string_len(str), string_content(str)); // 3, "Hi!"
string_del(str);
}
*/
typedef struct string_s {
size_t ssize, scap;
char *buf;
} string_t;
string_t *string_new(void);
string_t *string_new_cap(size_t cap);
int string_reserve(string_t *s, size_t cap);
void string_del(string_t *s);
char *string_steal(string_t *s);
int string_add_char(string_t *s, char elem);
int string_add_string(string_t *s1, string_t *s2);
void string_pop(string_t *s);
string_t *string_dup(string_t const *s);
string_t *string_concat(string_t const *l, string_t const *r);
#define string_len(s) ((s)->ssize)
#define string_cap(s) ((s)->scap)
#define string_content(s) ((s)->buf)
#define string_begin(s) ((s)->buf)
#define string_end(s) ((s)->buf + (s)->ssize)
#define string_last(s) ((s)->buf[(s)->ssize - 1])
#define string_for(itname, s) \
for (char *itname = string_begin((s)); itname != string_end((s)); ++itname)
#endif // STRING_H

View File

@ -0,0 +1,922 @@
#include "generator.h"
#include "lang.h"
#include "prepare.h"
static const char *rft2str[8] = {
[RQT_FUN] = "",
[RQT_FUN_2] = "",
[RQT_FUN_MY] = "(my) ",
[RQT_FUN_D] = "(D) ",
[RQT_DATA] = "",
[RQT_DATAV] = "(V) ",
[RQT_DATAB] = "(B) ",
[RQT_DATAM] = "(my) ",
};
#define IS_RQT_FUN2(rty) (((rty) == RQT_FUN_2) || ((rty) == RQT_FUN_D))
#define IS_RQT_FUNCTION(rty) ((rty) < RQT_DATA)
void request_print(request_t *req) {
printf("%s", string_content(req->obj_name));
if (req->has_default && req->has_val && (IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) {
printf(" => conflict: was/is data, is/was function\n");
}
int is_fun;
if (req->has_default) is_fun = IS_RQT_FUNCTION(req->def.rty);
else if (req->has_val) is_fun = IS_RQT_FUNCTION(req->val.rty);
else {
printf(" => (no value)\n");
return;
}
if (is_fun) {
printf(" => %sfunction", req->weak ? "weak " : "");
if (req->has_default) {
printf(" with %sdefault %s%s%s%s",
req->default_comment ? "commented " : "",
rft2str[req->def.rty],
string_content(req->def.fun.typ),
(req->def.rty == RQT_FUN_2) ? " -> " : "",
(req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : "");
}
if (req->has_val) {
printf(" with solved %s%s%s%s",
rft2str[req->val.rty],
string_content(req->val.fun.typ),
(req->val.rty == RQT_FUN_2) ? " -> " : "",
(req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : "");
}
} else {
printf(" => %sdata", req->weak ? "weak " : "");
if (req->has_default) {
if (req->def.dat.has_size) printf(" with default %zu", req->def.dat.sz);
else printf(" with no default");
}
if (req->has_val) {
if (req->def.dat.has_size) printf(" with solved %zu", req->def.dat.sz);
else printf(" with no solved");
}
}
printf("\n");
}
void request_print_check(request_t *req) {
if (req->has_default && req->has_val && (IS_RQT_FUNCTION(req->def.rty) != IS_RQT_FUNCTION(req->val.rty))) {
printf("%s => conflict: was/is data, is/was function\n", string_content(req->obj_name));
}
int is_fun;
if (req->has_default) is_fun = IS_RQT_FUNCTION(req->def.rty);
else if (req->has_val) is_fun = IS_RQT_FUNCTION(req->val.rty);
else {
printf(" => (no value)\n");
return;
}
if (is_fun) {
if (req->has_val) {
if (req->has_default) {
int similar = 1;
// if (req->def.rty != req->val.rty) similar = 0;
if (similar && strcmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ))) {
// similar = 0;
// TODO
if (req->def.rty == RQT_FUN_MY) {
similar =
string_len(req->def.fun.typ) >= 3 &&
string_content(req->def.fun.typ)[2] == 'E' &&
!strncmp(string_content(req->def.fun.typ), string_content(req->val.fun.typ), 2) &&
!strcmp(string_content(req->def.fun.typ) + 3, string_content(req->val.fun.typ) + 2);
if (similar) {
// We need to add the 'E' back here
}
} else {
similar = 0;
}
}
if (!similar) {
printf("%s => %sfunction with %sdefault %s%s%s%s and different solved %s%s%s%s\n",
string_content(req->obj_name),
req->weak ? "weak " : "",
req->default_comment ? "commented " : "",
rft2str[req->def.rty],
string_content(req->def.fun.typ),
(req->def.rty == RQT_FUN_2) ? " -> " : "",
(req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : "",
rft2str[req->val.rty],
string_content(req->val.fun.typ),
(req->val.rty == RQT_FUN_2) ? " -> " : "",
(req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : "");
}
} else {
printf("%s => %sfunction with solved %s%s%s%s\n",
string_content(req->obj_name),
req->weak ? "weak " : "",
rft2str[req->val.rty],
string_content(req->val.fun.typ),
(req->val.rty == RQT_FUN_2) ? " -> " : "",
(req->val.rty == RQT_FUN_2) ? string_content(req->val.fun.fun2) : "");
}
} else if (req->has_default) {
/* printf("%s => unsolved %sfunction with %sdefault %s%s%s%s\n",
string_content(req->obj_name),
req->weak ? "weak " : "",
req->default_comment ? "commented " : "",
rft2str[req->def.rty],
string_content(req->def.fun.typ),
(req->def.rty == RQT_FUN_2) ? " -> " : "",
(req->def.rty == RQT_FUN_2) ? string_content(req->def.fun.fun2) : ""); */
}
} else {
printf("%s => %sdata", string_content(req->obj_name), req->weak ? "weak " : "");
if (req->has_default) {
if (req->def.dat.has_size) printf(" with default %zu", req->def.dat.sz);
else printf(" with no default");
}
if (req->has_val) {
if (req->def.dat.has_size) printf(" with solved %zu", req->def.dat.sz);
else printf(" with no solved");
}
}
}
void request_del(request_t *req) {
string_del(req->obj_name);
if (req->has_default) {
switch (req->def.rty) {
case RQT_FUN: string_del(req->def.fun.typ); break;
case RQT_FUN_2: string_del(req->def.fun.typ); string_del(req->def.fun.fun2); break;
case RQT_FUN_MY: string_del(req->def.fun.typ); break;
case RQT_FUN_D: string_del(req->def.fun.typ); string_del(req->def.fun.fun2); break;
case RQT_DATA: break;
case RQT_DATAV: break;
case RQT_DATAB: break;
case RQT_DATAM: break;
}
}
if (req->has_val) {
switch (req->val.rty) {
case RQT_FUN: string_del(req->val.fun.typ); break;
case RQT_FUN_2: string_del(req->val.fun.typ); string_del(req->val.fun.fun2); break;
case RQT_FUN_MY: string_del(req->val.fun.typ); break;
case RQT_FUN_D: string_del(req->val.fun.typ); string_del(req->val.fun.fun2); break;
case RQT_DATA: break;
case RQT_DATAV: break;
case RQT_DATAB: break;
case RQT_DATAM: break;
}
}
}
static int valid_reqtype(string_t *t) {
const char *s = string_content(t);
if (!((s[0] >= 'A') && (s[0] <= 'Z')) && !((s[0] >= 'a') && (s[0] <= 'z'))) return 0;
if (s[1] != 'F') return 0;
for (size_t i = 2; i < string_len(t); ++i) {
if (!((s[i] >= 'A') && (s[i] <= 'Z')) && !((s[i] >= 'a') && (s[i] <= 'z'))) return 0;
}
return 1;
}
static const char *rqt_suffix[8] = {
[RQT_FUN] = "",
[RQT_FUN_2] = "2",
[RQT_FUN_MY] = "M",
[RQT_FUN_D] = "D",
[RQT_DATA] = "",
[RQT_DATAV] = "V",
[RQT_DATAB] = "B",
[RQT_DATAM] = "M",
};
static void request_output(FILE *f, request_t *req) {
if (!req->has_val) {
if (!req->has_default) {
// printf("Warning: %s has no value and no default, assuming function\n", string_content(req->obj_name));
fprintf(f, "//GO%s(%s, \n", req->weak ? "W" : "", string_content(req->obj_name));
} else if (IS_RQT_FUNCTION(req->def.rty)) {
fprintf(f, "%sGO%s%s(%s, %s%s%s%s%s)%s\n",
req->default_comment ? "//" : "",
req->weak ? "W" : "",
rqt_suffix[req->def.rty],
string_content(req->obj_name),
valid_reqtype(req->def.fun.typ) ? "" : "\"",
string_content(req->def.fun.typ),
valid_reqtype(req->def.fun.typ) ? "" : "\"",
IS_RQT_FUN2(req->def.rty) ? ", " : "",
IS_RQT_FUN2(req->def.rty) ? string_content(req->def.fun.fun2) : "",
req->default_comment ? "" : " // Warning: failed to confirm");
} else {
if (req->def.dat.has_size) {
fprintf(f, "%sDATA%s%s(%s, %zu) // Warning: failed to confirm\n",
req->default_comment ? "//" : "",
req->weak ? "W" : "",
rqt_suffix[req->def.rty],
string_content(req->obj_name),
req->def.dat.sz);
} else {
fprintf(f, "//DATA%s%s(%s, \n",
req->weak ? "W" : "",
rqt_suffix[req->def.rty],
string_content(req->obj_name));
}
}
} else {
if (IS_RQT_FUNCTION(req->val.rty)) {
int is_comment =
(req->has_default && !req->default_comment) ? (req->val.rty != req->def.rty) : (req->val.rty != RQT_FUN);
fprintf(f, "%sGO%s%s(%s, %s%s%s)\n",
is_comment ? "//" : "",
req->weak ? "W" : "",
rqt_suffix[req->val.rty],
string_content(req->obj_name),
string_content(req->val.fun.typ),
IS_RQT_FUN2(req->val.rty) ? ", " : "",
IS_RQT_FUN2(req->val.rty) ? req->val.fun.fun2 ? string_content(req->val.fun.fun2) : "<error: no val>" : "");
} else {
if (req->val.dat.has_size) {
int is_comment = !req->has_default || req->default_comment || (req->def.rty != req->val.rty);
fprintf(f, "%sDATA%s(%s, %zu)\n",
is_comment ? "//" : "",
rqt_suffix[req->val.rty],
string_content(req->obj_name),
req->val.dat.sz);
} else {
fprintf(f, "//DATA%s(%s, \n",
rqt_suffix[req->val.rty],
string_content(req->obj_name));
}
}
}
}
void output_from_requests(FILE *f, VECTOR(requests) *reqs) {
fprintf(f, "#if !(defined(GO) && defined(GOM) && defined(GO2) && defined(DATA))\n#error Meh...\n#endif\n\n");
vector_for(requests, req, reqs) {
request_output(f, req);
}
}
VECTOR_IMPL(requests, request_del)
VECTOR(requests) *requests_from_file(const char *filename, FILE *f) {
prepare_t *prep = prepare_new_file(f, filename);
if (!prep) {
printf("Failed to create the prepare structure for the requests file\n");
return NULL;
}
VECTOR(requests) *ret = vector_new(requests);
if (!ret) {
prepare_del(prep);
return NULL;
}
int lineno = 1;
// Ignore the first 3 lines
preproc_token_t tok;
do {
tok = pre_next_token(prep, 0);
if (tok.tokt == PPTOK_NEWLINE) ++lineno;
} while (!preproc_token_isend(&tok) && (lineno < 4));
// TODO: better conditionals handling
// Also, for now assume we have no definition
int if_depth = 0, entered_depth = 0;
while (1) {
int is_comment = 0;
tok = pre_next_token(prep, 1);
while (tok.tokt == PPTOK_START_LINE_COMMENT) {
is_comment = 1;
// Empty destructor
tok = pre_next_token(prep, 1);
}
if ((tok.tokt == PPTOK_SYM) && (tok.tokv.sym == SYM_HASH)) {
if (is_comment) {
preproc_token_del(&tok);
tok = pre_next_newline_token(prep); // Returns a newline
++lineno;
continue;
}
tok = pre_next_token(prep, 0);
if (tok.tokt != PPTOK_IDENT) {
printf("Error: invalid requests file: invalid preprocessor line\n");
preproc_token_del(&tok);
goto failed;
}
if (!strcmp(string_content(tok.tokv.str), "ifdef")) {
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
if (tok.tokt != PPTOK_IDENT) {
printf("Error: invalid requests file: invalid '#ifdef' line\n");
preproc_token_del(&tok);
goto failed;
}
++if_depth;
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
} else if (!strcmp(string_content(tok.tokv.str), "ifndef")) {
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
if (tok.tokt != PPTOK_IDENT) {
printf("Error: invalid requests file: invalid '#ifndef' line\n");
preproc_token_del(&tok);
goto failed;
}
if (if_depth == entered_depth) ++entered_depth;
++if_depth;
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
} else if (!strcmp(string_content(tok.tokv.str), "else")) {
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
if (if_depth == entered_depth + 1) ++entered_depth;
else if (if_depth == entered_depth) --entered_depth;
} else if (!strcmp(string_content(tok.tokv.str), "endif")) {
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
if (if_depth == entered_depth) --entered_depth;
--if_depth;
} else {
printf("Error: invalid requests file: invalid preprocessor command '%s'\n", string_content(tok.tokv.str));
string_del(tok.tokv.str);
goto failed;
}
while (!preproc_token_isend(&tok) && (tok.tokt != PPTOK_NEWLINE)) {
preproc_token_del(&tok);
tok = pre_next_token(prep, 0);
}
++lineno;
if (preproc_token_isend(&tok)) {
if (tok.tokt == PPTOK_EOF) goto success;
else {
preproc_token_del(&tok);
goto failed;
}
}
} else if (tok.tokt == PPTOK_NEWLINE) {
++lineno;
} else if (tok.tokt == PPTOK_EOF) {
goto success;
} else if ((tok.tokt == PPTOK_IDENT)
&& (!strcmp(string_content(tok.tokv.str), "GO")
|| !strcmp(string_content(tok.tokv.str), "GO2")
|| !strcmp(string_content(tok.tokv.str), "GOD")
|| !strcmp(string_content(tok.tokv.str), "GOM")
|| !strcmp(string_content(tok.tokv.str), "GOW")
|| !strcmp(string_content(tok.tokv.str), "GOW2")
|| !strcmp(string_content(tok.tokv.str), "GOWD")
|| !strcmp(string_content(tok.tokv.str), "GOWM"))) {
int isweak = (string_content(tok.tokv.str)[2] == 'W');
request_t req = {
.has_default = 0,
.default_comment = is_comment,
.has_val = 0,
.obj_name = NULL,
.weak = isweak,
.def = {
.rty =
(string_content(tok.tokv.str)[isweak ? 3 : 2] == '2') ? RQT_FUN_2 :
(string_content(tok.tokv.str)[isweak ? 3 : 2] == 'D') ? RQT_FUN_D :
(string_content(tok.tokv.str)[isweak ? 3 : 2] == 'M') ? RQT_FUN_MY : RQT_FUN,
.fun.typ = NULL,
.fun.fun2 = NULL,
},
};
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_LPAREN)) {
printf("Error: invalid requests file: invalid GO line %d (lparen)\n", lineno);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
if (tok.tokt != PPTOK_IDENT) {
printf("Error: invalid requests file: invalid GO line %d (obj_name)\n", lineno);
preproc_token_del(&tok);
goto failed;
}
req.obj_name = tok.tokv.str;
// Token moved
tok = pre_next_token(prep, 0);
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) {
printf("Error: invalid requests file: invalid GO line %d (comma)\n", lineno);
string_del(req.obj_name);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
if ((tok.tokt == PPTOK_IDENT) || (tok.tokt == PPTOK_STRING)) {
req.def.fun.typ = (tok.tokt == PPTOK_STRING) ? tok.tokv.sstr : tok.tokv.str;
req.has_default = 1;
// Token moved
tok = pre_next_token(prep, 0);
if ((req.def.rty == RQT_FUN_2) || (req.def.rty == RQT_FUN_D)) {
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) {
printf("Error: invalid requests file: invalid GO line %d (comma 2)\n", lineno);
string_del(req.obj_name);
string_del(req.def.fun.typ);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
if (tok.tokt != PPTOK_IDENT) {
printf("Error: invalid requests file: invalid GO line %d (redirect)\n", lineno);
string_del(req.obj_name);
string_del(req.def.fun.typ);
preproc_token_del(&tok);
goto failed;
}
req.def.fun.fun2 = tok.tokv.str;
// Token moved
tok = pre_next_token(prep, 0);
}
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) {
printf("Error: invalid requests file: invalid GO line %d (rparen)\n", lineno);
string_del(req.obj_name);
string_del(req.def.fun.typ);
if (req.def.fun.fun2) string_del(req.def.fun.fun2);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
}
if (tok.tokt != PPTOK_NEWLINE) {
printf("Error: invalid requests file: invalid GO line %d (newline)\n", lineno);
string_del(req.obj_name);
if (req.def.fun.typ) string_del(req.def.fun.typ);
if (req.def.fun.fun2) string_del(req.def.fun.fun2);
preproc_token_del(&tok);
goto failed;
}
if (if_depth == entered_depth) {
if (!vector_push(requests, ret, req)) {
printf("Error: failed to add request for %s\n", string_content(req.obj_name));
string_del(req.obj_name);
if (req.def.fun.typ) string_del(req.def.fun.typ);
if (req.def.fun.fun2) string_del(req.def.fun.fun2);
// Empty destructor
goto failed;
}
}
++lineno;
} else if ((tok.tokt == PPTOK_IDENT)
&& (!strcmp(string_content(tok.tokv.str), "DATA")
|| !strcmp(string_content(tok.tokv.str), "DATAV")
|| !strcmp(string_content(tok.tokv.str), "DATAB")
|| !strcmp(string_content(tok.tokv.str), "DATAM"))) {
request_t req = {
.has_default = 1,
.default_comment = is_comment,
.has_val = 0,
.obj_name = NULL,
.weak = (string_content(tok.tokv.str)[4] == 'V'),
.def = {
.rty =
(string_content(tok.tokv.str)[4] == 'V') ? RQT_DATAV :
(string_content(tok.tokv.str)[4] == 'B') ? RQT_DATAB :
(string_content(tok.tokv.str)[4] == 'M') ? RQT_DATAM : RQT_DATA,
.dat.has_size = 0,
.dat.sz = 0,
},
};
string_del(tok.tokv.str);
tok = pre_next_token(prep, 0);
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_LPAREN)) {
printf("Error: invalid requests file: invalid DATA line %d (lparen)\n", lineno);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
if (tok.tokt != PPTOK_IDENT) {
printf("Error: invalid requests file: invalid DATA line %d (obj_name)\n", lineno);
preproc_token_del(&tok);
goto failed;
}
req.obj_name = tok.tokv.str;
// Token moved
tok = pre_next_token(prep, 0);
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_COMMA)) {
printf("Error: invalid requests file: invalid DATA line %d (comma)\n", lineno);
string_del(req.obj_name);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
if (tok.tokt == PPTOK_NUM) {
num_constant_t cst;
if (!num_constant_convert(tok.tokv.str, &cst)) {
printf("Error: invalid requests file: invalid DATA line %d (num conversion)\n", lineno);
string_del(req.obj_name);
preproc_token_del(&tok);
goto failed;
}
switch (cst.typ) {
case NCT_FLOAT:
case NCT_DOUBLE:
case NCT_LDOUBLE:
printf("Error: invalid requests file: invalid DATA line %d (num conversion)\n", lineno);
string_del(req.obj_name);
preproc_token_del(&tok);
goto failed;
case NCT_INT32: req.def.dat.sz = (size_t)cst.val.i32; break;
case NCT_UINT32: req.def.dat.sz = (size_t)cst.val.u32; break;
case NCT_INT64: req.def.dat.sz = (size_t)cst.val.i64; break;
case NCT_UINT64: req.def.dat.sz = (size_t)cst.val.u64; break;
}
req.def.dat.has_size = 1;
// Token moved
tok = pre_next_token(prep, 0);
if ((tok.tokt != PPTOK_SYM) || (tok.tokv.sym != SYM_RPAREN)) {
printf("Error: invalid requests file: invalid DATA line %d (rparen)\n", lineno);
string_del(req.obj_name);
preproc_token_del(&tok);
goto failed;
}
// Empty destructor
tok = pre_next_token(prep, 0);
}
if (tok.tokt != PPTOK_NEWLINE) {
printf("Error: invalid requests file: invalid DATA line %d (newline)\n", lineno);
string_del(req.obj_name);
preproc_token_del(&tok);
goto failed;
}
if (if_depth == entered_depth) {
if (!vector_push(requests, ret, req)) {
printf("Error: failed to add request for %s\n", string_content(req.obj_name));
string_del(req.obj_name);
// Empty destructor
goto failed;
}
}
++lineno;
} else {
if (is_comment) {
preproc_token_del(&tok);
tok = pre_next_newline_token(prep); // Returns a newline
++lineno;
continue;
}
printf("Error: invalid requests file: invalid token:\n");
preproc_token_print(&tok);
preproc_token_del(&tok);
goto failed;
}
}
failed:
prepare_del(prep);
vector_del(requests, ret);
return NULL;
success:
prepare_del(prep);
return ret;
}
static int is_simple_type_ptr_to(type_t *typ, int *needs_D, int *needs_my) {
switch (typ->typ) {
case TYPE_BUILTIN:
return 1; // Assume pointers to builtin are simple
case TYPE_ARRAY:
if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple
return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my);
case TYPE_STRUCT_UNION:
if (typ->val.st->explicit_simple) return 1;
if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK
if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque
typ->_internal_use = 1;
for (size_t i = 0; i < typ->val.st->nmembers; ++i) {
st_member_t *mem = &typ->val.st->members[i];
if (!is_simple_type_ptr_to(mem->typ, needs_D, needs_my)) {
typ->_internal_use = 0;
return 0;
}
}
typ->_internal_use = 0;
return 1;
case TYPE_ENUM:
return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my);
case TYPE_PTR:
return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my);
case TYPE_FUNCTION:
*needs_my = 1;
return 1;
default:
printf("Error: is_simple_type_ptr_to on unknown type %u\n", typ->typ);
return 0;
}
}
static int is_simple_type(type_t *typ, int *needs_D, int *needs_my) {
switch (typ->typ) {
case TYPE_BUILTIN:
return 1; // Assume pointers to builtin are simple
case TYPE_ARRAY:
if (typ->val.array.array_sz == (size_t)-1) return 0; // VLA are not simple
return is_simple_type_ptr_to(typ->val.array.typ, needs_D, needs_my);
case TYPE_STRUCT_UNION:
if (typ->val.st->explicit_simple) return 1;
if (typ->_internal_use) return 1; // Recursive structures are OK as long as every other members are OK
// if (!typ->val.st->is_defined) return 1; // Undefined structures are OK since they are opaque
// To be safe, don't allow opaque structures
if (!typ->val.st->is_defined) return 0;
typ->_internal_use = 1;
for (size_t i = 0; i < typ->val.st->nmembers; ++i) {
st_member_t *mem = &typ->val.st->members[i];
if (!is_simple_type(mem->typ, needs_D, needs_my)) {
typ->_internal_use = 0;
return 0;
}
}
typ->_internal_use = 0;
return 1;
case TYPE_ENUM:
return is_simple_type(typ->val.typ, needs_D, needs_my);
case TYPE_PTR:
return is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my);
case TYPE_FUNCTION:
// Functions should be handled differently (GO instead of DATA)
return 0;
default:
printf("Error: is_simple_type on unknown type %u\n", typ->typ);
return 0;
}
}
static int convert_type(string_t *dest, type_t *typ, int is_ret, int *needs_D, int *needs_my, string_t *obj_name) {
if (typ->is_atomic) {
printf("TODO: convert_type for atomic types\n");
return 0;
}
switch (typ->typ) {
case TYPE_BUILTIN: {
int has_char = 0;
char c;
switch (typ->val.builtin) {
case BTT_VOID: has_char = 1; c = 'v'; break;
case BTT_BOOL: has_char = 1; c = 'i'; break;
case BTT_CHAR: has_char = 1; c = 'c'; break;
case BTT_SCHAR: has_char = 1; c = 'c'; break;
case BTT_UCHAR: has_char = 1; c = 'C'; break;
case BTT_SHORT: has_char = 1; c = 'w'; break;
case BTT_SSHORT: has_char = 1; c = 'w'; break;
case BTT_USHORT: has_char = 1; c = 'W'; break;
case BTT_INT: has_char = 1; c = 'i'; break;
case BTT_SINT: has_char = 1; c = 'i'; break;
case BTT_UINT: has_char = 1; c = 'u'; break;
case BTT_LONG: has_char = 1; c = 'l'; break;
case BTT_SLONG: has_char = 1; c = 'l'; break;
case BTT_ULONG: has_char = 1; c = 'L'; break;
case BTT_LONGLONG: has_char = 1; c = 'I'; break;
case BTT_SLONGLONG: has_char = 1; c = 'I'; break;
case BTT_ULONGLONG: has_char = 1; c = 'U'; break;
case BTT_INT128: has_char = 1; c = 'H'; break; // TODO: Is 'H' for signed and unsigned?
case BTT_SINT128: has_char = 1; c = 'H'; break; // Is 'H' for signed and unsigned?
case BTT_UINT128: has_char = 1; c = 'H'; break; // Is 'H' for signed and unsigned?
case BTT_S8: has_char = 1; c = 'c'; break;
case BTT_U8: has_char = 1; c = 'C'; break;
case BTT_S16: has_char = 1; c = 'w'; break;
case BTT_U16: has_char = 1; c = 'W'; break;
case BTT_S32: has_char = 1; c = 'i'; break;
case BTT_U32: has_char = 1; c = 'u'; break;
case BTT_S64: has_char = 1; c = 'I'; break;
case BTT_U64: has_char = 1; c = 'U'; break;
case BTT_FLOAT: has_char = 1; c = 'f'; break;
case BTT_CFLOAT: has_char = 1; c = 'x'; break;
case BTT_IFLOAT: has_char = 1; c = 'f'; break;
case BTT_DOUBLE: has_char = 1; c = 'd'; break;
case BTT_CDOUBLE: has_char = 1; c = 'X'; break;
case BTT_IDOUBLE: has_char = 1; c = 'd'; break;
case BTT_LONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break;
case BTT_CLONGDOUBLE: *needs_D = 1; has_char = 1; c = 'Y'; break;
case BTT_ILONGDOUBLE: *needs_D = 1; has_char = 1; c = 'D'; break;
case BTT_VA_LIST: has_char = 1; c = 'A'; break;
default:
printf("Error: convert_type on unknown builtin %u\n", typ->val.builtin);
return 0;
}
if (has_char) {
if (!string_add_char(dest, c)) {
printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
return 0;
}
return 1;
} else {
printf("Internal error: unknown state builtin=%u\n", typ->val.builtin);
return 0;
} }
case TYPE_ARRAY:
printf("Error: convert_type on raw array\n");
return 0;
case TYPE_STRUCT_UNION:
if (!typ->is_validated || typ->is_incomplete) {
printf("Error: incomplete return type for %s\n", string_content(obj_name));
return 0;
}
if (is_ret) {
if (typ->szinfo.size <= 8) {
if (!string_add_char(dest, 'U')) {
printf("Error: failed to add type char for structure return\n");
return 0;
}
return 1;
} else if (typ->szinfo.size <= 16) {
if (!string_add_char(dest, 'H')) {
printf("Error: failed to add type char for large structure return\n");
return 0;
}
return 1;
} else {
if (!string_add_char(dest, 'p')) {
printf("Error: failed to add type char for very large structure return\n");
return 0;
}
return 1;
}
} else {
if (typ->val.st->nmembers == 1) {
return convert_type(dest, typ->val.st->members[0].typ, is_ret, needs_D, needs_my, obj_name);
}
printf("TODO: convert_type on structure as argument (%s)\n", string_content(obj_name));
return 0;
}
case TYPE_ENUM:
return convert_type(dest, typ->val.typ, is_ret, needs_D, needs_my, obj_name);
case TYPE_PTR:
if ((typ->val.typ->typ == TYPE_STRUCT_UNION) && typ->val.typ->val.st->tag && !strcmp(string_content(typ->val.typ->val.st->tag), "_IO_FILE")) {
if (!string_add_char(dest, 'S')) {
printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
return 0;
}
return 1;
}
if (is_simple_type_ptr_to(typ->val.typ, needs_D, needs_my)) {
if (!string_add_char(dest, 'p')) {
printf("Error: failed to add type char for simple pointer\n");
return 0;
}
return 1;
} else {
*needs_my = 1;
if (!string_add_char(dest, 'p')) {
printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
return 0;
}
return 1;
}
case TYPE_FUNCTION:
printf("Error: convert_type on raw function\n");
return 0;
default:
printf("Error: convert_type on unknown type %u\n", typ->typ);
return 0;
}
}
static int convert_type_post(string_t *dest, type_t *typ, string_t *obj_name) {
if (typ->is_atomic) {
printf("TODO: convert_type_post for atomic types\n");
return 0;
}
switch (typ->typ) {
case TYPE_BUILTIN: return 1;
case TYPE_ARRAY: return 1;
case TYPE_STRUCT_UNION:
if (!typ->is_validated || typ->is_incomplete) {
printf("Error: incomplete return type for %s\n", string_content(obj_name));
return 0;
}
if (typ->szinfo.size <= 16) {
return 1;
} else {
if (!string_add_char(dest, 'p')) {
printf("Error: failed to add type char for very large structure return as parameter\n");
return 0;
}
return 2;
}
case TYPE_ENUM: return 1;
case TYPE_PTR: return 1;
case TYPE_FUNCTION: return 1;
default:
printf("Error: convert_type_post on unknown type %u\n", typ->typ);
return 0;
}
}
int solve_request(request_t *req, type_t *typ) {
if (typ->typ == TYPE_FUNCTION) {
int needs_D = 0, needs_my = req->has_default && (req->def.rty == RQT_FUN_MY), needs_2 = 0;
int convert_post;
req->val.fun.typ = string_new();
if (!req->val.fun.typ) {
printf("Error: failed to create function type string\n");
return 0;
}
if (!convert_type(req->val.fun.typ, typ->val.fun.ret, 1, &needs_D, &needs_my, req->obj_name)) goto fun_fail;
if (!string_add_char(req->val.fun.typ, 'F')) {
printf("Error: failed to add convention char\n");
goto fun_fail;
}
if (req->has_default && (req->def.rty == RQT_FUN_MY) && (string_content(req->def.fun.typ)[2] == 'E')) {
if (!string_add_char(req->val.fun.typ, 'E')) {
printf("Error: failed to add emu char\n");
goto fun_fail;
}
}
convert_post = convert_type_post(req->val.fun.typ, typ->val.fun.ret, req->obj_name);
if (!convert_post) goto fun_fail;
if (typ->val.fun.nargs == (size_t)-1) {
printf("Warning: assuming empty specification is void specification\n");
if (convert_post == 1) {
if (!string_add_char(req->val.fun.typ, 'v')) {
printf("Error: failed to add void specification char\n");
goto fun_fail;
}
}
} else if (!typ->val.fun.nargs && !typ->val.fun.has_varargs) {
if (convert_post == 1) {
if (!string_add_char(req->val.fun.typ, 'v')) {
printf("Error: failed to add void specification char\n");
goto fun_fail;
}
}
} else {
for (size_t i = 0; i < typ->val.fun.nargs; ++i) {
if (!convert_type(req->val.fun.typ, typ->val.fun.args[i], 0, &needs_D, &needs_my, req->obj_name)) goto fun_fail;
}
if (typ->val.fun.has_varargs) {
if (!string_add_char(req->val.fun.typ, 'V')) {
printf("Error: failed to add type char for %s\n", builtin2str[typ->val.builtin]);
goto fun_fail;
}
}
}
// fun_succ:
if (req->has_default && (req->def.rty == RQT_FUN_2) && !needs_my) {
needs_2 = 1;
req->val.fun.fun2 = string_dup(req->def.fun.fun2);
if (!req->val.fun.fun2) {
printf("Error: failed to duplicate string (request for function %s with default redirection)\n", string_content(req->obj_name));
return 0;
}
} else if (req->has_default && (req->def.rty == RQT_FUN_D) && !needs_my) {
needs_2 = 0;
req->val.fun.fun2 = string_dup(req->def.fun.fun2);
if (!req->val.fun.fun2) {
printf("Error: failed to duplicate string (request for function %s with long double types)\n", string_content(req->obj_name));
return 0;
}
} else if (!needs_my && needs_D) {
req->val.fun.fun2 = string_new();
if (!req->val.fun.fun2) {
printf("Error: failed to create empty string (request for function %s with long double types)\n", string_content(req->obj_name));
return 0;
}
}
req->val.rty =
needs_my ? RQT_FUN_MY :
needs_2 ? RQT_FUN_2 :
needs_D ? RQT_FUN_D : RQT_FUN;
req->has_val = 1;
return 1;
fun_fail:
string_del(req->val.fun.typ);
return 0;
} else {
int needs_D = 0, needs_my = req->has_default && (req->def.rty == RQT_FUN_MY);
if (is_simple_type(typ, &needs_D, &needs_my)) {
// TODO: Hmm...
req->val.rty = needs_my ? RQT_DATAM : req->has_default ? req->def.rty : req->weak ? RQT_DATAV : RQT_DATA;
req->val.dat.has_size = 1;
req->val.dat.sz = typ->szinfo.size;
req->has_val = 1;
return 1;
} else {
printf("Error: TODO: solve_request for data %s with non-simple type ", string_content(req->obj_name));
type_print(typ);
printf("\n");
return 0;
}
}
}
int solve_request_map(request_t *req, khash_t(type_map) *decl_map) {
khiter_t it = kh_get(type_map, decl_map, string_content(req->obj_name));
if (it == kh_end(decl_map)) {
if (string_content(req->obj_name)[0] != '_') {
printf("Error: %s was not declared\n", string_content(req->obj_name));
}
return 0;
}
return solve_request(req, kh_val(decl_map, it));
}
int solve_requests(VECTOR(requests) *reqs, khash_t(type_map) *decl_map) {
int ret = 1;
vector_for(requests, req, reqs) {
if (!solve_request_map(req, decl_map)) ret = 0;
}
return ret;
}

View File

@ -0,0 +1,50 @@
#pragma once
#ifndef GENERATOR_H
#define GENERATOR_H
#include <stdio.h>
#include "cstring.h"
#include "lang.h"
typedef struct request_s {
string_t *obj_name;
_Bool has_default, default_comment;
_Bool has_val;
_Bool weak;
struct {
enum request_type_e {
RQT_FUN,
RQT_FUN_2,
RQT_FUN_MY,
RQT_FUN_D,
RQT_DATA,
RQT_DATAV,
RQT_DATAB,
RQT_DATAM,
} rty;
union {
struct {
string_t *typ;
string_t *fun2;
} fun;
struct {
int has_size;
size_t sz;
} dat;
};
} def, val;
} request_t;
VECTOR_DECLARE(requests, request_t)
void request_print(request_t *req);
void request_print_check(request_t *req);
void output_from_requests(FILE *f, VECTOR(requests) *reqs);
VECTOR(requests) *requests_from_file(const char *filename, FILE *f); // Takes ownership of f
int solve_request(request_t *req, type_t *typ);
int solve_request_map(request_t *req, khash_t(type_map) *decl_map);
int solve_requests(VECTOR(requests) *reqs, khash_t(type_map) *decl_map);
#endif // GENERATOR_H

699
wrapperhelper/src/khash.h Normal file
View File

@ -0,0 +1,699 @@
/* The MIT License
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
An example:
#include "khash.h"
KHASH_MAP_INIT_INT(32, char)
int main() {
int ret, is_missing;
khiter_t k;
khash_t(32) *h = kh_init(32);
k = kh_put(32, h, 5, &ret);
kh_value(h, k) = 10;
k = kh_get(32, h, 10);
is_missing = (k == kh_end(h));
k = kh_get(32, h, 5);
kh_del(32, h, k);
for (k = kh_begin(h); k != kh_end(h); ++k)
if (kh_exist(h, k)) kh_value(h, k) = 1;
kh_destroy(32, h);
return 0;
}
*/
/*
2013-05-02 (0.2.8):
* Use quadratic probing. When the capacity is power of 2, stepping function
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
hashing on cache performance and is more robust than linear probing.
In theory, double hashing should be more robust than quadratic probing.
However, my implementation is probably not for large hash tables, because
the second hash function is closely tied to the first hash function,
which reduce the effectiveness of double hashing.
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
2011-12-29 (0.2.7):
* Minor code clean up; no actual effect.
2011-09-16 (0.2.6):
* The capacity is a power of 2. This seems to dramatically improve the
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
- http://code.google.com/p/ulib/
- http://nothings.org/computer/judy/
* Allow to optionally use linear probing which usually has better
performance for random input. Double hashing is still the default as it
is more robust to certain non-random input.
* Added Wang's integer hash function (not used by default). This hash
function is more robust to certain non-random input.
2011-02-14 (0.2.5):
* Allow to declare global functions.
2009-09-26 (0.2.4):
* Improve portability
2008-09-19 (0.2.3):
* Corrected the example
* Improved interfaces
2008-09-11 (0.2.2):
* Improved speed a little in kh_put()
2008-09-10 (0.2.1):
* Added kh_clear()
* Fixed a compiling error
2008-09-02 (0.2.0):
* Changed to token concatenation which increases flexibility.
2008-08-31 (0.1.2):
* Fixed a bug in kh_get(), which has not been tested previously.
2008-08-31 (0.1.1):
* Added destructor
*/
#ifndef __AC_KHASH_H
#define __AC_KHASH_H
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic ignored "-Wconversion"
/*!
@header
Generic hash table library.
*/
#define AC_VERSION_KHASH_H "0.2.8"
#include <stdlib.h>
#include <string.h>
#include <limits.h>
/* compiler specific configuration */
#if UINT_MAX == 0xffffffffu
typedef unsigned int khint32_t;
#elif ULONG_MAX == 0xffffffffu
typedef unsigned long khint32_t;
#endif
#if ULONG_MAX == ULLONG_MAX
typedef unsigned long khint64_t;
#else
typedef unsigned long long khint64_t;
#endif
#ifdef _MSC_VER
#define kh_inline __inline
#else
#define kh_inline inline
#endif
typedef khint32_t khint_t;
typedef khint_t khiter_t;
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1u<<((i&0xfU)<<1)))
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2u<<((i&0xfU)<<1)))
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3u<<((i&0xfU)<<1)))
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1u<<((i&0xfU)<<1))
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
#ifndef kroundup32
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
#endif
#ifndef kcalloc
#define kcalloc(N,Z) calloc(N,Z)
#endif
#ifndef kmalloc
#define kmalloc(Z) malloc(Z)
#endif
#ifndef krealloc
#define krealloc(P,Z) realloc(P,Z)
#endif
#ifndef kfree
#define kfree(P) free(P)
#endif
static const double __ac_HASH_UPPER = 0.77;
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct kh_##name##_s{ \
khint_t n_buckets, size, n_occupied, upper_bound; \
khint32_t *flags; \
khkey_t *keys; \
khval_t *vals; \
} kh_##name##_t;
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
extern kh_##name##_t *kh_init_##name(void); \
extern void kh_destroy_##name(kh_##name##_t *h); \
extern void kh_clear_##name(kh_##name##_t *h); \
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
SCOPE kh_##name##_t *kh_init_##name(void) { \
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
} \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
kfree((void *)h->keys); kfree(h->flags); \
kfree((void *)h->vals); \
kfree(h); \
} \
} \
SCOPE void kh_clear_##name(kh_##name##_t *h) \
{ \
if (h && h->flags) { \
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
h->size = h->n_occupied = 0; \
} \
} \
__attribute__((pure)) SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
{ \
if (h->n_buckets) { \
khint_t k, i, last, mask, step = 0; \
mask = h->n_buckets - 1; \
k = __hash_func(key); i = k & mask; \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
i = (i + (++step)) & mask; \
if (i == last) return h->n_buckets; \
} \
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
} else return 0; \
} \
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
khint32_t *new_flags = 0; \
khint_t j = 1; \
{ \
kroundup32(new_n_buckets); \
if (new_n_buckets < 4) new_n_buckets = 4; \
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
else { /* hash table size to be changed (shrink or expand); rehash */ \
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (!new_flags) return -1; \
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
if (h->n_buckets < new_n_buckets) { /* expand */ \
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (!new_keys) { kfree(new_flags); return -1; } \
h->keys = new_keys; \
if (kh_is_map) { \
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
if (!new_vals) { kfree(new_flags); return -1; } \
h->vals = new_vals; \
} \
} /* otherwise shrink */ \
} \
} \
if (j) { /* rehashing is needed */ \
for (j = 0; j != h->n_buckets; ++j) { \
if (__ac_iseither(h->flags, j) == 0) { \
khkey_t key = h->keys[j]; \
khval_t val; \
khint_t new_mask; \
new_mask = new_n_buckets - 1; \
if (kh_is_map) val = h->vals[j]; \
__ac_set_isdel_true(h->flags, j); \
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
khint_t k, i, step = 0; \
k = __hash_func(key); \
i = k & new_mask; \
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
__ac_set_isempty_false(new_flags, i); \
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
} else { /* write the element and jump out of the loop */ \
h->keys[i] = key; \
if (kh_is_map) h->vals[i] = val; \
break; \
} \
} \
} \
} \
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
} \
kfree(h->flags); /* free the working space */ \
h->flags = new_flags; \
h->n_buckets = new_n_buckets; \
h->n_occupied = h->size; \
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
} \
return 0; \
} \
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
{ \
khint_t x; \
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
if (h->n_buckets > (h->size<<1)) { \
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
*ret = -1; return h->n_buckets; \
} \
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
*ret = -1; return h->n_buckets; \
} \
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
{ \
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
else { \
last = i; \
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
if (__ac_isdel(h->flags, i)) site = i; \
i = (i + (++step)) & mask; \
if (i == last) { x = site; break; } \
} \
if (x == h->n_buckets) { \
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
else x = i; \
} \
} \
} \
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; ++h->n_occupied; \
*ret = 1; \
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
h->keys[x] = key; \
__ac_set_isboth_false(h->flags, x); \
++h->size; \
*ret = 2; \
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
return x; \
} \
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
{ \
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
__ac_set_isdel_true(h->flags, x); \
--h->size; \
} \
}
#define KHASH_DECLARE(name, khkey_t, khval_t) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
__KHASH_TYPE(name, khkey_t, khval_t) \
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
/* --- BEGIN OF HASH FUNCTIONS --- */
/*! @function
@abstract Integer hash function
@param key The integer [khint32_t]
@return The hash value [khint_t]
*/
#define kh_int_hash_func(key) (khint32_t)(key)
/*! @function
@abstract Integer comparison function
*/
#define kh_int_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract 64-bit integer hash function
@param key The integer [khint64_t]
@return The hash value [khint_t]
*/
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
/*! @function
@abstract 64-bit integer comparison function
*/
#define kh_int64_hash_equal(a, b) ((a) == (b))
/*! @function
@abstract const char* hash function
@param s Pointer to a null terminated string
@return The hash value
*/
__attribute__((pure)) static kh_inline khint_t __ac_X31_hash_string(const char *s)
{
khint_t h = (khint_t)*s;
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
return h;
}
/*! @function
@abstract Another interface to const char* hash function
@param key Pointer to a null terminated string [const char*]
@return The hash value [khint_t]
*/
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
/*! @function
@abstract Const char* comparison function
*/
#define kh_str_hash_equal(a, b) ((!a && !b) || (a && b && strcmp(a, b) == 0))
static kh_inline khint_t __ac_Wang_hash(khint_t key)
{
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
/* --- END OF HASH FUNCTIONS --- */
/* Other convenient macros... */
/*!
@abstract Type of the hash table.
@param name Name of the hash table [symbol]
*/
#define khash_t(name) kh_##name##_t
/*! @function
@abstract Initiate a hash table.
@param name Name of the hash table [symbol]
@return Pointer to the hash table [khash_t(name)*]
*/
#define kh_init(name) kh_init_##name()
/*! @function
@abstract Destroy a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_destroy(name, h) kh_destroy_##name(h)
/*! @function
@abstract Reset a hash table without deallocating memory.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
*/
#define kh_clear(name, h) kh_clear_##name(h)
/*! @function
@abstract Resize a hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param s New size [khint_t]
*/
#define kh_resize(name, h, s) kh_resize_##name(h, s)
/*! @function
@abstract Insert a key to the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@param r Extra return code: 0 if the key is present in the hash table;
1 if the bucket is empty (never used); 2 if the element in
the bucket has been deleted [int*]
@return Iterator to the inserted element [khint_t]
*/
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
/*! @function
@abstract Retrieve a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Key [type of keys]
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
*/
#define kh_get(name, h, k) kh_get_##name(h, k)
/*! @function
@abstract Remove a key from the hash table.
@param name Name of the hash table [symbol]
@param h Pointer to the hash table [khash_t(name)*]
@param k Iterator to the element to be deleted [khint_t]
*/
#define kh_del(name, h, k) kh_del_##name(h, k)
/*! @function
@abstract Test whether a bucket contains data.
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return 1 if containing data; 0 otherwise [int]
*/
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
/*! @function
@abstract Get key given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Key [type of keys]
*/
#define kh_key(h, x) ((h)->keys[x])
/*! @function
@abstract Get value given an iterator
@param h Pointer to the hash table [khash_t(name)*]
@param x Iterator to the bucket [khint_t]
@return Value [type of values]
@discussion For hash sets, calling this results in segfault.
*/
#define kh_val(h, x) ((h)->vals[x])
/*! @function
@abstract Alias of kh_val()
*/
#define kh_value(h, x) ((h)->vals[x])
/*! @function
@abstract Get the start iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The start iterator [khint_t]
*/
#define kh_begin(h) (khint_t)(0)
/*! @function
@abstract Get the end iterator
@param h Pointer to the hash table [khash_t(name)*]
@return The end iterator [khint_t]
*/
#define kh_end(h) ((h)->n_buckets)
/*! @function
@abstract Get the number of elements in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of elements in the hash table [khint_t]
*/
#define kh_size(h) ((h)->size)
/*! @function
@abstract Get the number of buckets in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@return Number of buckets in the hash table [khint_t]
*/
#define kh_n_buckets(h) ((h)->n_buckets)
/*! @function
@abstract Iterate over the entries in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
(vvar) = kh_val(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the entries in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param code Block of code to execute
*/
#define kh_foreach_key(h, kvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the values in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param vvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(vvar) = kh_val(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the entries in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param kvar Variable to which key will be assigned
@param rvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_key_value_ref(h, kvar, rvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(kvar) = kh_key(h,__i); \
(rvar) = &kh_val(h,__i); \
code; \
} }
/*! @function
@abstract Iterate over the values in the hash table
@param h Pointer to the hash table [khash_t(name)*]
@param rvar Variable to which value will be assigned
@param code Block of code to execute
*/
#define kh_foreach_value_ref(h, rvar, code) { khint_t __i; \
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
if (!kh_exist(h,__i)) continue; \
(rvar) = &kh_val(h,__i); \
code; \
} }
/* More conenient interfaces */
/*! @function
@abstract Instantiate a hash set containing integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT(name) \
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
#define KHASH_SET_DECLARE_INT(name) \
KHASH_DECLARE(name, khint32_t, char)
#define KHASH_SET_IMPL_INT(name) \
__KHASH_IMPL(name, , khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT(name, khval_t) \
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
#define KHASH_MAP_DECLARE_INT(name, khval_t) \
KHASH_DECLARE(name, khint32_t, khval_t)
#define KHASH_MAP_IMPL_INT(name, khval_t) \
__KHASH_IMPL(name, , khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_INT64(name) \
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
#define KHASH_SET_DECLARE_INT64(name) \
KHASH_DECLARE(name, khint64_t, char)
#define KHASH_SET_IMPL_INT64(name) \
__KHASH_IMPL(name, , khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
/*! @function
@abstract Instantiate a hash map containing 64-bit integer keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_INT64(name, khval_t) \
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
#define KHASH_MAP_DECLARE_INT64(name, khval_t) \
KHASH_DECLARE(name, khint64_t, khval_t)
#define KHASH_MAP_IMPL_INT64(name, khval_t) \
__KHASH_IMPL(name, , khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
typedef const char *kh_cstr_t;
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
*/
#define KHASH_SET_INIT_STR(name) \
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
#define KHASH_SET_DECLARE_STR(name) \
KHASH_DECLARE(name, kh_cstr_t, char)
#define KHASH_SET_IMPL_STR(name) \
__KHASH_IMPL(name, , kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
/*! @function
@abstract Instantiate a hash map containing const char* keys
@param name Name of the hash table [symbol]
@param khval_t Type of values [type]
*/
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#define KHASH_MAP_DECLARE_STR(name, khval_t) \
KHASH_DECLARE(name, kh_cstr_t, khval_t)
#define KHASH_MAP_IMPL_STR(name, khval_t) \
__KHASH_IMPL(name, , kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
#pragma GCC diagnostic pop
#endif /* __AC_KHASH_H */

1164
wrapperhelper/src/lang.c Normal file

File diff suppressed because it is too large Load Diff

439
wrapperhelper/src/lang.h Normal file
View File

@ -0,0 +1,439 @@
#pragma once
#ifndef LANG_H
#define LANG_H
#include "cstring.h"
#include "khash.h"
#include "vector.h"
#define LONG_IS_32BITS 0
enum token_sym_type_e {
SYM_LBRACKET,
SYM_RBRACKET,
SYM_LSQBRACKET,
SYM_RSQBRACKET,
SYM_LPAREN,
SYM_RPAREN,
SYM_HASH,
SYM_HASHHASH,
SYM_SEMICOLON,
SYM_COLON,
SYM_COLONCOLON,
SYM_VARIADIC,
SYM_QUESTION,
SYM_DOT,
SYM_DASHGT,
SYM_TILDE,
SYM_EXCL,
SYM_PLUS,
SYM_DASH,
SYM_STAR,
SYM_SLASH,
SYM_PERCENT,
SYM_HAT,
SYM_AMP,
SYM_PIPE,
SYM_EQ,
SYM_PLUSEQ,
SYM_DASHEQ,
SYM_STAREQ,
SYM_SLASHEQ,
SYM_PERCENTEQ,
SYM_HATEQ,
SYM_AMPEQ,
SYM_PIPEEQ,
SYM_EQEQ,
SYM_EXCLEQ,
SYM_LT,
SYM_GT,
SYM_LTEQ,
SYM_GTEQ,
SYM_AMPAMP,
SYM_PIPEPIPE,
SYM_LTLT,
SYM_GTGT,
SYM_LTLTEQ,
SYM_GTGTEQ,
SYM_PLUSPLUS,
SYM_DASHDASH,
SYM_COMMA,
};
#define LAST_SYM SYM_COMMA
typedef struct preproc_token_s {
enum preproc_token_e {
PPTOK_INVALID = 0,
PPTOK_IDENT, // Expandable ident
PPTOK_IDENT_UNEXP, // Unexpandable ident
PPTOK_NUM,
PPTOK_STRING,
PPTOK_INCL,
PPTOK_SYM,
PPTOK_NEWLINE,
PPTOK_BLANK,
PPTOK_START_LINE_COMMENT,
PPTOK_EOF
} tokt;
union {
string_t *str;
struct {
string_t *sstr; // The string literal content
int sisstr; // 0 for '' or <>, 1 for ""
};
char c;
enum token_sym_type_e sym;
} tokv;
} preproc_token_t;
VECTOR_DECLARE(preproc, preproc_token_t)
void preproc_token_del(preproc_token_t *tok);
enum token_keyword_type_e {
KW_ALIGNAS = 0,
KW_ALIGNOF,
KW_ATOMIC,
KW_AUTO,
KW_BOOL,
KW_BREAK,
KW_CASE,
KW_CHAR,
KW_COMPLEX,
KW_CONST,
KW_CONTINUE,
KW_DEFAULT,
KW_DO,
KW_DOUBLE,
KW_ELSE,
KW_ENUM,
KW_EXTERN,
KW_FLOAT,
KW_FOR,
KW_GENERIC,
KW_GOTO,
KW_IF,
KW_IMAGINARY,
KW_INLINE,
KW_INT,
KW_INT128,
KW_LONG,
KW_NORETURN,
KW_REGISTER,
KW_RESTRICT,
KW_RETURN,
KW_SHORT,
KW_SIGNED,
KW_SIZEOF,
KW_STATIC,
KW_STATIC_ASSERT,
KW_STRUCT,
KW_SWITCH,
KW_THREAD_LOCAL,
KW_TYPEDEF,
KW_UNION,
KW_UNSIGNED,
KW_VOID,
KW_VOLATILE,
KW_WHILE,
};
#define LAST_KEYWORD KW_WHILE
typedef struct proc_token_s {
enum proc_token_e {
PTOK_INVALID = 0,
PTOK_IDENT,
PTOK_KEYWORD,
PTOK_NUM,
PTOK_STRING,
PTOK_SYM,
PTOK_PRAGMA,
PTOK_EOF
} tokt;
union proc_token_val_u {
string_t *str;
struct {
string_t *sstr; // The string literal content
int sisstr; // 0 for '' or <>, 1 for ""
};
char c;
enum token_sym_type_e sym;
enum token_keyword_type_e kw;
struct {
enum proc_pragma_e {
PRAGMA_ALLOW_INTS,
PRAGMA_MARK_SIMPLE,
} typ;
string_t *val;
} pragma;
} tokv;
} proc_token_t;
VECTOR_DECLARE(proc, proc_token_t)
void proc_token_del(proc_token_t *tok);
typedef struct num_constant_s {
enum num_constant_e {
NCT_FLOAT,
NCT_DOUBLE,
NCT_LDOUBLE,
NCT_INT32,
NCT_UINT32,
NCT_INT64,
NCT_UINT64,
} typ;
union {
float f;
double d;
long double l;
int32_t i32;
uint32_t u32;
int64_t i64;
uint64_t u64;
} val;
} num_constant_t;
int num_constant_convert(string_t *str, num_constant_t *cst);
KHASH_MAP_DECLARE_STR(const_map, num_constant_t)
typedef struct expr_s {
enum expr_type_e {
ETY_VAR,
ETY_CONST,
// ETY_GENERIC,
ETY_CALL,
ETY_ACCESS,
ETY_PTRACCESS, // Convertible to DEREF + ACCESS
ETY_UNARY,
ETY_BINARY,
ETY_TERNARY,
// ETY_INIT_LIST,
ETY_CAST,
} typ;
union {
string_t *var;
num_constant_t cst;
// TODO: _Generic
struct {
struct expr_s *fun, **args;
size_t nargs;
} call;
struct {
struct expr_s *val;
string_t *member;
} access;
struct {
enum unary_op_e {
UOT_POSTINCR,
UOT_POSTDECR,
UOT_PREINCR,
UOT_PREDECR,
UOT_REF,
UOT_POS,
UOT_NEG,
UOT_DEREF,
UOT_ANOT, // Arithmetic not, ie '~'
UOT_BNOT, // Boolean not, ie '!'
} typ;
struct expr_s *e;
} unary;
struct {
enum binary_op_e {
BOT_ADD,
BOT_SUB,
BOT_MUL,
BOT_DIV,
BOT_MOD,
BOT_LSH,
BOT_RSH,
BOT_LT,
BOT_GT,
BOT_LE,
BOT_GE,
BOT_EQ,
BOT_NE,
BOT_AAND,
BOT_AXOR,
BOT_AOR,
BOT_BAND,
BOT_BOR,
BOT_ASSGN_EQ,
BOT_ASSGN_ADD,
BOT_ASSGN_SUB,
BOT_ASSGN_MUL,
BOT_ASSGN_DIV,
BOT_ASSGN_MOD,
BOT_ASSGN_LSH,
BOT_ASSGN_RSH,
BOT_ASSGN_AAND,
BOT_ASSGN_AXOR,
BOT_ASSGN_AOR,
BOT_COMMA,
BOT_ARRAY, // Convertible to DEREF + ADD
} typ;
struct expr_s *e1, *e2;
} binary;
struct {
enum ternary_op_e {
TOT_COND,
} typ;
struct expr_s *e1, *e2, *e3;
} ternary;
// TODO: (type){init}
struct {
struct type_s *typ;
struct expr_s *e;
} cast;
} val;
} expr_t;
void expr_del(expr_t *e);
typedef struct size_info_s {
size_t size, align;
} size_info_t;
typedef struct type_s {
struct {
unsigned is_atomic : 1;
unsigned is_const : 1;
unsigned is_restrict : 1;
unsigned is_volatile : 1;
unsigned is_incomplete : 1; // \ The type needs to be complete and
unsigned is_validated : 1; // / validated for the size_info to be populated
unsigned _internal_use : 1;
};
size_t nrefs;
enum type_type_e {
TYPE_BUILTIN, // builtin
TYPE_ARRAY, // array
TYPE_STRUCT_UNION, // st
TYPE_ENUM, // typ which points to TYPE_BUILTIN
TYPE_PTR, // typ
TYPE_FUNCTION, // fun
} typ;
union {
enum type_builtin_e {
BTT_VOID,
BTT_BOOL,
BTT_CHAR,
BTT_SCHAR,
BTT_UCHAR,
BTT_SHORT,
BTT_SSHORT,
BTT_USHORT,
BTT_INT,
BTT_SINT,
BTT_UINT,
BTT_LONG,
BTT_SLONG,
BTT_ULONG,
BTT_LONGLONG,
BTT_SLONGLONG,
BTT_ULONGLONG,
BTT_INT128,
BTT_SINT128,
BTT_UINT128,
BTT_S8,
BTT_U8,
BTT_S16,
BTT_U16,
BTT_S32,
BTT_U32,
BTT_S64,
BTT_U64,
#define BTT_START_INT_EXT BTT_S8
#define BTT_INT_EXTS "__int8_t", "__uint8_t", "__int16_t", "__uint16_t", "__int32_t", "__uint32_t", "__int64_t", "__uint64_t"
BTT_FLOAT,
BTT_CFLOAT,
BTT_IFLOAT,
BTT_DOUBLE,
BTT_CDOUBLE,
BTT_IDOUBLE,
BTT_LONGDOUBLE,
BTT_CLONGDOUBLE,
BTT_ILONGDOUBLE,
BTT_VA_LIST,
} builtin;
#define LAST_BUILTIN BTT_VA_LIST
struct type_s *typ;
struct {
struct type_s *typ;
size_t array_sz; // -1 for VLA
} array;
struct struct_s *st;
struct {
struct type_s *ret;
size_t nargs; // -1 for no specification
struct type_s **args;
int has_varargs;
} fun;
} val;
size_info_t szinfo;
} type_t;
void type_del(type_t *typ);
KHASH_MAP_DECLARE_STR(type_map, type_t*)
void type_map_del(khash_t(type_map) *map);
int type_t_equal(type_t*, type_t*);
typedef struct st_member_s {
string_t *name; // May be NULL
type_t *typ;
_Bool is_bitfield;
size_t bitfield_width;
} st_member_t;
typedef struct struct_s {
string_t *tag;
int is_defined;
size_t nrefs;
int is_struct; // 0 = union, 1 = struct
int has_incomplete; // 1 if the last element of the structure is a VLA or if an element of the union recursively contains a VLA
int explicit_simple;
size_t nmembers;
st_member_t *members;
} struct_t;
void st_member_del(st_member_t *member);
void struct_del(struct_t *st);
KHASH_MAP_DECLARE_STR(struct_map, struct_t*)
type_t *type_new(void); // Create a new (complete) builtin type
type_t *type_new_ptr(type_t *target); // Create a new pointer type; doesn't increment the use counter of the target
// type_t *type_do_copy(type_t *ref); // Always duplicate ref; decrements the use counter
// type_t *type_do_copy_nodec(const type_t *ref); // Always duplicate ref; doesn't decrements the use counter
// type_t *type_maybe_copy(type_t *ref); // Only duplicate ref if it is used elsewhere; in that case, decrements the use counter
int type_copy_into(type_t *dest, const type_t *ref); // Copy ref into dest, keeping additional qualifiers and without changing any use counter
struct_t *struct_new(int is_struct, string_t *tag); // Create a new struct
// Try to merge some types with other types; this may delete ptr and increase a use counter in a type referenced by the table
KHASH_DECLARE(type_set, type_t*, char)
type_t *type_try_merge(type_t *ptr, khash_t(type_set) *set);
extern const char *builtin2str[LAST_BUILTIN + 1];
void type_print(type_t *typ);
void struct_print(const struct_t *st);
typedef struct file_s {
khash_t(struct_map) *struct_map;
khash_t(type_map) *type_map;
khash_t(type_map) *enum_map;
khash_t(type_map) *decl_map;
type_t *builtins[LAST_BUILTIN + 1];
khash_t(const_map) *const_map;
khash_t(type_set) *type_set;
} file_t;
file_t *file_new(void);
void file_del(file_t *f);
extern const char *sym2str[LAST_SYM + 1];
extern const char *kw2str[LAST_KEYWORD + 1];
void preproc_token_print(const preproc_token_t *tok);
int preproc_token_isend(const preproc_token_t *tok);
void proc_token_print(const proc_token_t *tok);
int proc_token_iserror(const proc_token_t *tok);
int proc_token_isend(const proc_token_t *tok);
KHASH_MAP_DECLARE_STR(str2kw, enum token_keyword_type_e)
extern khash_t(str2kw) *str2kw;
int init_str2kw(void);
void del_str2kw(void);
#endif // LANG_H

184
wrapperhelper/src/main.c Normal file
View File

@ -0,0 +1,184 @@
#include <err.h>
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include "generator.h"
#include "lang.h"
#include "parse.h"
#include "khash.h"
static void help(char *arg0) {
printf("Usage: %s --help\n"
" %s [--prepare|--preproc|--proc] <filename_in>\n"
" %s <filename_in> <filename_reqs> <filename_out>\n"
"\n"
" --prepare Dump all preprocessor tokens (prepare phase)\n"
" --preproc Dump all processor tokens (preprocessor phase)\n"
" --proc Dump all typedefs, declarations and constants (processor phase)\n"
"\n"
" <filename_in> Parsing file\n"
" <filename_reqs> Reference file (example: wrappedlibc_private.h)\n"
" <filename_out> Output file\n",
arg0, arg0, arg0);
}
enum main_state {
MAIN_RUN,
MAIN_PREPARE,
MAIN_PREPROC,
MAIN_PROC,
};
int main(int argc, char **argv) {
setbuf(stdout, NULL);
if (!setlocale(LC_NUMERIC, "C")) {
printf("Error: failed to set LC_NUMERIC to C\n");
return 2;
}
enum main_state ms;
int off;
if ((argc == 2) && !strcmp(argv[1], "--help")) {
help(argv[0]);
return 0;
} else if (argc == 2) {
ms = MAIN_PROC;
off = 1;
} else if ((argc == 3) && !strcmp(argv[1], "--prepare")) {
ms = MAIN_PREPARE;
off = 2;
} else if ((argc == 3) && !strcmp(argv[1], "--preproc")) {
ms = MAIN_PREPROC;
off = 2;
} else if ((argc == 3) && !strcmp(argv[1], "--proc")) {
ms = MAIN_PROC;
off = 2;
} else if (argc == 4) {
ms = MAIN_RUN;
off = 1;
} else {
help(argv[0]);
return 2;
}
if (!init_str2kw()) {
return 2;
}
FILE *f = fopen(argv[off], "r");
if (!f) {
err(2, "Error: failed to open %s", argv[off]);
return 2;
}
switch (ms) {
case MAIN_RUN: {
file_t *content = parse_file(argv[off], f); // Takes ownership of f
if (!content) {
printf("Error: failed to parse the file\n");
del_str2kw();
return 0;
}
FILE *ref = fopen(argv[off + 1], "r");
if (!ref) {
err(2, "Error: failed to open %s", argv[off + 1]);
del_str2kw();
return 2;
}
VECTOR(requests) *reqs = requests_from_file(argv[off + 1], ref);
if (!reqs) {
file_del(content);
del_str2kw();
return 2;
}
// vector_for(requests, req, reqs) request_print(req);
if (!solve_requests(reqs, content->decl_map)) {
printf("Warning: failed to solve all default requests\n");
}
// vector_for(requests, req, reqs) request_print(req);
//vector_for(requests, req, reqs) request_print_check(req);
FILE *out = fopen(argv[off + 2], "w");
if (!out) {
err(2, "Error: failed to open %s", argv[off + 1]);
file_del(content);
vector_del(requests, reqs);
del_str2kw();
return 2;
}
output_from_requests(out, reqs);
fclose(out);
vector_del(requests, reqs);
file_del(content);
del_str2kw();
return 0; }
case MAIN_PROC: {
file_t *content = parse_file(argv[off], f); // Takes ownership of f
if (!content) {
printf("Error: failed to parse the file\n");
del_str2kw();
return 0;
}
// print content
const char *name;
struct_t *st;
type_t *typ;
num_constant_t cst;
/* for (enum type_builtin_e i = 0; i < LAST_BUILTIN; ++i) {
printf("Builtin %u: %p, ", i, content->builtins[i]);
type_print(content->builtins[i]);
printf("\n");
} */
kh_foreach(content->struct_map, name, st,
printf("Struct: %s -> %p = ", name, st);
struct_print(st);
printf("\n")
)
kh_foreach(content->type_map, name, typ,
printf("Typedef: %s -> %p = ", name, typ);
type_print(typ);
printf("\n")
)
kh_foreach(content->enum_map, name, typ,
printf("Enum: %s -> %p = ", name, typ);
type_print(typ);
printf("\n")
)
kh_foreach(content->const_map, name, cst,
printf("Constant: %s -> ", name);
switch (cst.typ) {
case NCT_FLOAT: printf("%ff", cst.val.f); break;
case NCT_DOUBLE: printf("%f", cst.val.d); break;
case NCT_LDOUBLE: printf("%Lfl", cst.val.l); break;
case NCT_INT32: printf("%d", cst.val.i32); break;
case NCT_UINT32: printf("%uu", cst.val.u32); break;
case NCT_INT64: printf("%ldll", cst.val.i64); break;
case NCT_UINT64: printf("%lullu", cst.val.u64); break;
}
printf("\n")
)
kh_foreach(content->decl_map, name, typ,
printf("Declaration: %s -> %p = ", name, typ);
type_print(typ);
printf("\n")
)
file_del(content);
del_str2kw();
return 0; }
case MAIN_PREPARE:
dump_prepare(argv[off], f); // Takes ownership of f
del_str2kw();
return 0;
case MAIN_PREPROC:
dump_preproc(argv[off], f); // Takes ownership of f
del_str2kw();
return 0;
}
printf("<internal error> Failed to run mode %u\n", ms);
return 2;
}

3164
wrapperhelper/src/parse.c Normal file

File diff suppressed because it is too large Load Diff

14
wrapperhelper/src/parse.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#ifndef PARSE_H
#define PARSE_H
#include <stdio.h>
#include "lang.h"
void dump_prepare(const char *filename, FILE *file);
void dump_preproc(const char *filename, FILE *file);
file_t *parse_file(const char *filename, FILE *file);
#endif // PARSE_H

372
wrapperhelper/src/prepare.c Normal file
View File

@ -0,0 +1,372 @@
#include "prepare.h"
#include <string.h>
struct prepare_s {
FILE *f;
int buf[4];
int buf_len; // <= 4 (though 3 *should* be enough)
char *srcn;
enum prepare_state {
PREPST_NONE = 0,
PREPST_NL,
PREPST_HASH,
PREPST_INCL,
PREPST_DEF,
PREPST_DEFID,
} st;
};
prepare_t *prepare_new_file(FILE *f, const char *filename) {
prepare_t *ret = malloc(sizeof *ret);
if (!ret) {
fclose(f);
return NULL;
}
*ret = (prepare_t){
.f = f,
.buf = {0, 0, 0},
.buf_len = 0,
.srcn = strdup(filename),
.st = PREPST_NL,
};
return ret;
}
void prepare_del(prepare_t *prep) {
if (prep->f) fclose(prep->f);
if (prep->srcn) free(prep->srcn);
free(prep);
}
static int get_char(prepare_t *src) {
start_get_char:
int c = src->buf_len ? src->buf[--src->buf_len] : getc(src->f);
src->buf_len = 0;
if (c == '\\') {
c = src->buf_len ? src->buf[--src->buf_len] : getc(src->f);
if (c == '\n') goto start_get_char;
src->buf[src->buf_len++] = c;
return '\\';
}
return c;
}
// Do not call this more than twice in a row if the last character retrieved is '\\'
static void unget_char(prepare_t *src, int c) {
src->buf[src->buf_len++] = c;
}
static void fill_ident(prepare_t *src, string_t *buf) {
while (1) {
int c = get_char(src);
if ((c == '_') || ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) {
string_add_char(buf, (char)c);
} else {
unget_char(src, c);
return;
}
}
}
static void fill_num(prepare_t *src, string_t *buf) {
int started_exp = 0;
while (1) {
int c = get_char(src);
if ((c == '_') || (c == '.') || ((c >= '0') && (c <= '9')) || ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))
|| (started_exp && ((c == '+') || (c == '-')))) {
started_exp = (c == 'e') || (c == 'E') || (c == 'p') || (c == 'P');
string_add_char(buf, (char)c);
} else {
unget_char(src, c);
return;
}
}
}
static void fill_str(prepare_t *src, string_t *buf, char end_c, int can_esc) {
int has_esc = 0;
while (1) {
int c = get_char(src);
if (has_esc && (c >= 0) && (c <= 0x7F) && (c != '\n')) {
// Not technically standard compliant (should support \ooo, \x..., \u..., \U...)
// Since we don't really care about parsing the content, only the delimiters, this is good enough
string_add_char(buf, '\\');
string_add_char(buf, (char)c);
has_esc = 0;
} else if (c == '\\') {
if (can_esc) {
has_esc = 1;
} else {
string_add_char(buf, '\\');
}
} else if ((c >= 0) && (c <= 0x7F) && (c != end_c)) {
has_esc = 0;
string_add_char(buf, (char)c);
} else {
if (has_esc) {
// c is invalid or a '\n', or can_esc = 0 and c = end_c
string_add_char(buf, '\\');
}
if (c != end_c)
unget_char(src, c);
return;
}
}
}
#define BASE_NSYMS 25
static const struct symbs_s {
char c;
enum token_sym_type_e sym;
int nnext;
const struct symbs_s *next;
} *symbs = (struct symbs_s[BASE_NSYMS]){
#define TERM(ch, t) { .c = ch, .sym = t, .nnext = 0, .next = NULL }
#define NONTERM(ch, t, n, ...) { .c = ch, .sym = t, .nnext = n, .next = (struct symbs_s[n]){__VA_ARGS__} }
// Only '..' must have a sym > LAST_SYM; change next_token if this is not the case
NONTERM('.', SYM_DOT, 1, NONTERM('.', LAST_SYM + 1, 1, TERM('.', SYM_VARIADIC))),
TERM('{', SYM_LBRACKET),
TERM('}', SYM_RBRACKET),
TERM('[', SYM_LSQBRACKET),
TERM(']', SYM_RSQBRACKET),
TERM('(', SYM_LPAREN),
TERM(')', SYM_RPAREN),
NONTERM('#', SYM_HASH, 1, TERM('#', SYM_HASHHASH)),
TERM(';', SYM_SEMICOLON),
NONTERM(':', SYM_COLON, 1, TERM(':', SYM_COLONCOLON)),
TERM('?', SYM_QUESTION),
TERM('~', SYM_TILDE),
NONTERM('!', SYM_EXCL, 1, TERM('=', SYM_EXCLEQ)),
NONTERM('+', SYM_PLUS, 2, TERM('=', SYM_PLUSEQ), TERM('+', SYM_PLUSPLUS)),
NONTERM('-', SYM_DASH, 3, TERM('=', SYM_DASHEQ), TERM('-', SYM_DASHDASH), TERM('>', SYM_DASHGT)),
NONTERM('*', SYM_STAR, 1, TERM('=', SYM_STAREQ)),
NONTERM('/', SYM_SLASH, 1, TERM('=', SYM_SLASHEQ)),
NONTERM('%', SYM_PERCENT, 1, TERM('=', SYM_PERCENTEQ)),
NONTERM('^', SYM_HAT, 1, TERM('=', SYM_HATEQ)),
NONTERM('&', SYM_AMP, 2, TERM('=', SYM_AMPEQ), TERM('&', SYM_AMPAMP)),
NONTERM('|', SYM_PIPE, 2, TERM('=', SYM_PIPEEQ), TERM('|', SYM_PIPEPIPE)),
NONTERM('=', SYM_EQ, 1, TERM('=', SYM_EQEQ)),
NONTERM('<', SYM_LT, 2, TERM('=', SYM_LTEQ), NONTERM('<', SYM_LTLT, 1, TERM('=', SYM_LTLTEQ))),
NONTERM('>', SYM_GT, 2, TERM('=', SYM_GTEQ), NONTERM('>', SYM_GTGT, 1, TERM('=', SYM_GTGTEQ))),
TERM(',', SYM_COMMA),
#undef NONTERM
#undef TERM
};
preproc_token_t pre_next_token(prepare_t *src, int allow_comments) {
start_next_token:
int c = get_char(src);
if (c == EOF) {
if (src->st == PREPST_NL) {
return (preproc_token_t){
.tokt = PPTOK_EOF,
.tokv.c = (char)c
};
} else {
// Force newline at EOF
unget_char(src, c);
src->st = PREPST_NL;
return (preproc_token_t){
.tokt = PPTOK_NEWLINE,
.tokv.c = (char)c
};
}
}
if (src->st == PREPST_INCL && (c == '<')) {
src->st = PREPST_NONE;
preproc_token_t ret;
ret.tokt = PPTOK_INCL;
ret.tokv.sisstr = 0;
ret.tokv.sstr = string_new();
fill_str(src, ret.tokv.sstr, '>', 0);
return ret;
}
if (c == '\'') {
src->st = PREPST_NONE;
preproc_token_t ret;
ret.tokt = PPTOK_STRING;
ret.tokv.sisstr = 0;
ret.tokv.sstr = string_new_cap(1); // Usually only one character is inside a char literal
fill_str(src, ret.tokv.sstr, '\'', 1);
return ret;
}
if (c == '"') {
preproc_token_t ret;
ret.tokt = (src->st == PREPST_INCL) ? PPTOK_INCL : PPTOK_STRING;
src->st = PREPST_NONE;
ret.tokv.sisstr = 1;
ret.tokv.sstr = string_new();
fill_str(src, ret.tokv.sstr, '"', ret.tokt == PPTOK_STRING);
return ret;
}
if ((c == ' ') || (c == '\f') || (c == '\t') || (c == '\v')) {
if (src->st == PREPST_DEFID) {
src->st = PREPST_NONE;
return (preproc_token_t){
.tokt = PPTOK_BLANK,
.tokv.c = (char)c
};
} else goto start_next_token;
}
if (c == '\n') {
src->st = PREPST_NL;
return (preproc_token_t){
.tokt = PPTOK_NEWLINE,
.tokv.c = (char)c
};
}
if ((c == '_') || ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) {
preproc_token_t ret;
ret.tokt = PPTOK_IDENT;
ret.tokv.str = string_new_cap(1);
string_add_char(ret.tokv.str, (char)c);
fill_ident(src, ret.tokv.str);
src->st =
((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "include"))) ? PREPST_INCL :
((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "include_next"))) ? PREPST_INCL :
((src->st == PREPST_HASH) && (!strcmp(string_content(ret.tokv.str), "define"))) ? PREPST_DEF :
(src->st == PREPST_DEF) ? PREPST_DEFID :
PREPST_NONE;
return ret;
}
if ((c >= '0') && (c <= '9')) {
src->st = PREPST_NONE;
preproc_token_t ret;
ret.tokt = PPTOK_NUM;
ret.tokv.str = string_new_cap(1);
string_add_char(ret.tokv.str, (char)c);
fill_num(src, ret.tokv.str);
return ret;
}
if (c == '.') {
c = get_char(src);
if ((c >= '0') && (c <= '9')) {
src->st = PREPST_NONE;
preproc_token_t ret;
ret.tokt = PPTOK_NUM;
ret.tokv.str = string_new_cap(2);
string_add_char(ret.tokv.str, '.');
string_add_char(ret.tokv.str, (char)c);
fill_num(src, ret.tokv.str);
return ret;
} else {
unget_char(src, c);
c = '.';
}
}
if (c == '/') {
c = get_char(src);
if (c == '/') {
if (allow_comments) {
src->st = PREPST_NONE;
return (preproc_token_t){
.tokt = PPTOK_START_LINE_COMMENT,
.tokv.c = '/'
};
}
do {
c = get_char(src);
} while ((c != EOF) && (c != '\n'));
if (c != EOF) {
if (src->st == PREPST_NL)
goto start_next_token;
else {
src->st = PREPST_NL;
return (preproc_token_t){
.tokt = PPTOK_NEWLINE,
.tokv.c = (char)c
};
}
}
src->st = PREPST_NONE;
printf("Unfinished comment while preparing %s\n", src->srcn);
return (preproc_token_t){
.tokt = PPTOK_INVALID,
.tokv.c = (char)c
};
} else if (c == '*') {
c = get_char(src);
int last_star = 0;
while ((c != EOF) && (!last_star || (c != '/'))) {
last_star = c == '*';
c = get_char(src);
}
if (c != EOF) goto start_next_token;
src->st = PREPST_NONE;
printf("Unfinished comment while preparing %s\n", src->srcn);
return (preproc_token_t){
.tokt = PPTOK_INVALID,
.tokv.c = (char)c
};
} else {
unget_char(src, c);
c = '/';
}
}
struct symbs_s const *sym = NULL;
for (int i = 0; i < BASE_NSYMS; ++i) {
if (c == symbs[i].c) {
sym = &symbs[i];
break;
}
}
if (sym) {
while (sym->nnext) {
c = get_char(src);
int found = 0;
for (int i = 0; i < sym->nnext; ++i) {
if (c == sym->next[i].c) {
found = 1;
sym = &sym->next[i];
break;
}
}
if (!found) {
unget_char(src, c);
break;
}
}
if (sym->sym == LAST_SYM + 1) {
unget_char(src, sym->c);
sym = &symbs[0]; // This is where no check is made (see comment in the definition of symbs)
}
src->st = ((src->st == PREPST_NL) && (sym->sym == SYM_HASH)) ? PREPST_HASH : PREPST_NONE;
return (preproc_token_t){
.tokt = PPTOK_SYM,
.tokv.sym = sym->sym
};
}
src->st = PREPST_NONE;
printf("Invalid character 0x%X (%c) while preparing %s\n", (unsigned)c, (c >= 0x20) && (c < 127) ? c : '?', src->srcn);
return (preproc_token_t){
.tokt = PPTOK_INVALID,
.tokv.c = (char)c
};
}
preproc_token_t pre_next_newline_token(prepare_t *src) {
start_next_token:
int c = get_char(src);
if (c == EOF) {
// Force newline at EOF
unget_char(src, c);
src->st = PREPST_NL;
return (preproc_token_t){
.tokt = PPTOK_NEWLINE,
.tokv.c = (char)c
};
}
if (c == '\n') {
src->st = PREPST_NL;
return (preproc_token_t){
.tokt = PPTOK_NEWLINE,
.tokv.c = (char)c
};
}
goto start_next_token;
}

View File

@ -0,0 +1,18 @@
#pragma once
#ifndef PREPARE_H
#define PREPARE_H
#include <stdio.h>
#include "cstring.h"
#include "lang.h"
typedef struct prepare_s prepare_t;
prepare_t *prepare_new_file(FILE *f, const char *filename); // Takes ownership of f
void prepare_del(prepare_t *src);
preproc_token_t pre_next_token(prepare_t *src, int allow_comments);
preproc_token_t pre_next_newline_token(prepare_t *src); // In a comment ignore everything until the EOL or EOF
#endif // PREPARE_H

2901
wrapperhelper/src/preproc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
#pragma once
#ifndef PREPROC_H
#define PREPROC_H
#include <stdio.h>
#include "lang.h"
typedef struct preproc_s preproc_t;
preproc_t *preproc_new_file(FILE *f, char *dirname, const char *filename); // Takes ownership of f and dirname
proc_token_t proc_next_token(preproc_t *src);
int proc_unget_token(preproc_t *src, proc_token_t *tok);
void preproc_del(preproc_t *src);
#endif // PREPROC_H

105
wrapperhelper/src/vector.c Normal file
View File

@ -0,0 +1,105 @@
#include "vector.h"
VECTOR_IMPL(voidp, (void))
VECTOR_IMPL(char, (void))
VECTOR_IMPL(charp, (void))
static void stringp_del(string_t **s) { return string_del(*s); }
VECTOR_IMPL(string, stringp_del)
VECTOR(voidp) *vector_new_impl(void) {
VECTOR(voidp) *ret = malloc(sizeof(*ret));
if (!ret) return NULL;
ret->vsize = ret->vcap = 0; ret->content_v = NULL;
return ret;
}
VECTOR(voidp) *vector_new_cap_impl(size_t elem_size, size_t cap) {
VECTOR(voidp) *ret = malloc(sizeof(*ret));
if (!ret) return NULL;
cap = (cap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : cap * 2;
ret->content_v = malloc(cap * elem_size);
if (!ret->content_v) {
free(ret);
return NULL;
}
ret->vcap = cap;
ret->vsize = 0;
return ret;
}
int vector_reserve_impl(VECTOR(voidp) *v, size_t elem_size, size_t cap) {
size_t new_cap = (cap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : cap;
if (new_cap <= v->vcap) return 1;
void *new_content_v = realloc(v->content_v, elem_size * new_cap);
if (!new_content_v) return 0;
v->content_v = new_content_v;
v->vcap = new_cap;
return 1;
}
int vector_trim_impl(VECTOR(voidp) *v, size_t elem_size) {
if (v->vsize) {
void *new_content_v = realloc(v->content_v, elem_size * v->vsize);
if (!new_content_v) return 0;
v->content_v = new_content_v;
v->vcap = v->vsize;
return 1;
} else {
free(v->content_v);
v->content_v = NULL;
v->vcap = 0;
return 1;
}
}
void vector_common_pop_impl(VECTOR(voidp) *v, size_t elem_size) {
if (--v->vsize < v->vcap / 4) {
size_t new_cap = (v->vcap / 2 < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : v->vcap / 2;
if (new_cap == v->vcap) return;
void *new_content_v = realloc(v->content_v, elem_size * new_cap);
if (!new_content_v) return; // We don't really care if the realloc fails, we just need to not update anything
v->content_v = new_content_v;
v->vcap = new_cap;
}
}
void vector_common_popn_impl(VECTOR(voidp) *v, size_t n, size_t elem_size) {
if (n > v->vsize) n = v->vsize;
v->vsize -= n;
if (v->vsize < v->vcap / 4) {
size_t new_cap = v->vcap / 2;
while (v->vsize < new_cap / 4) new_cap /= 2;
new_cap = (new_cap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : new_cap;
if (new_cap == v->vcap) return;
void *new_content_v = realloc(v->content_v, elem_size * new_cap);
if (!new_content_v) return; // We don't really care if the realloc fails, we just need to not update anything
v->content_v = new_content_v;
v->vcap = new_cap;
}
}
void vector_common_clear_impl(VECTOR(voidp) *v) {
if (!v->content_v) return;
free(v->content_v);
v->vsize = v->vcap = 0; v->content_v = NULL;
}
int vector_push_vec_impl(VECTOR(voidp) *v1, VECTOR(voidp) *v2, size_t start, size_t len, size_t elem_size) {
if (start >= v2->vsize) return 1;
if (start + len > v2->vsize) len = v2->vsize - start;
if (!len) return 1;
if (v1->vsize + len > v1->vcap) {
size_t new_cap = (v1->vcap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : v1->vcap * 2;
while (v1->vsize + len > new_cap) {
new_cap = new_cap * 2;
}
void *new_content_v = realloc(v1->content_v, elem_size * new_cap);
if (!new_content_v) return 0;
v1->content_v = new_content_v;
v1->vcap = new_cap;
}
memcpy((char*)v1->content_v + elem_size * v1->vsize, (char*)v2->content_v + elem_size * start, elem_size * len);
v1->vsize += len;
return 1;
}

213
wrapperhelper/src/vector.h Normal file
View File

@ -0,0 +1,213 @@
#pragma once
#ifndef VECTOR_H
#define VECTOR_H
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "cstring.h"
// Note: do not use with empty types; simply use ints in this case.
/** Thread-unsafe vector implementation
* USAGE:
* ======
* VECTOR_DECLARE ----------- Declare a new vector type. Takes the name and the element type.
* VECTOR_IMPL -------------- Implements required functions for a vector type. Takes the name and the destructor of a pointer to the element.
* VECTOR_DECLARE_STATIC ---- Declare a new vector type with static implementation. Takes the name and the element type.
* VECTOR_IMPL_STATIC ------- Implements required functions for a vector type. Takes the name and the destructor of a pointer to the element.
* Functions are declared static.
* VECTOR_DEL --------------- Macro that takes the vector name and gives a function taking a vector and deletes it.
* VECTOR ------------------- The vector type. Takes the name.
* VECTOR_ELEM -------------- The element type. Takes the name.
* vector_new --------------- Creates a new vector. Takes the name.
* vector_new_cap ----------- Creates a new vector with a given capacity. Takes the name and the capacity.
* vector_reserve ----------- Ensures a vector has at least a given capacity. Takes the name, the vector and the capacity. May not reduce the vector capacity.
* vector_trim -------------- Ensures a vector has a capacity equal to its size. Takes the name, the vector. May reduce the vector capacity.
* vector_del --------------- Frees a vector. Takes the name and the vector. Destructs the content of the vector.
* vector_steal ------------- Frees a vector and returns the content. Takes the name and the vector. May reduce the vector capacity.
* vector_del_freed --------- Frees a vector without freeing the content. Takes the name and the vector. Does not interact with the content of the vector.
* vector_del_free_from ----- Frees a vector without freeing all of the content. Takes the name, the vector and the first element to free.
* Destroys part of the content of the vector.
* vector_push -------------- Push a new element. Takes the name, the vector and the new element. Does not fail if enough capacity remains.
* vector_push_vec ---------- Push a vector of new elements. Takes the name, the vector and the new element vector. Does not fail if enough capacity remains.
* vector_push_vec_slice ---- Push a slice of a vector of elements. Takes the name, the vector the new element vector, start and size.
* Does not fail if enough capacity remains.
* vector_pop --------------- Pops the last element. Takes the name and the vector. May reduce the vector capacity.
* vector_pop_nodel --------- Pops the last element without freeing it. Takes the name and the vector. May reduce the vector capacity.
* vector_pop_slice --------- Pops the last N element. Takes the name, the vector and the number of elements to remove.
* vector_pop_nodel_slice --- Pops the last N element without freeing them. Takes the name, the vector and the number of elements to remove.
* May reduce the vector capacity.
* vector_clear ------------- Remove every element. Takes the name and the vector.
* vector_clear_nodel ------- Remove every element without freeing them. Takes the name and the vector.
* vector_remove ------------ Removes an element. Takes the name, the vector and the element number. May reduce the vector capacity.
* vector_size -------------- Vector size (number of elements). Takes the name and the vector.
* vector_cap --------------- Vector capacity (number of elements). Takes the name and the vector.
* vector_content ----------- Pointer to the vector content. Takes the name and the vector.
* vector_begin ------------- Start of the vector content. Takes the name and the vector.
* vector_end --------------- End of the vector content. Points to unmanaged memory. Takes the name and the vector.
* vector_last -------------- Last element of the vector. Points to invalid memory if size is zero. Takes the name and the vector.
* vector_for --------------- Iterate over the elements of a vector. This is a for loop. Takes the name, the iterator name and the vector.
*
* VECTOR_DEL(name)(v) is equivalent to vector_del(name, v).
* Predefined vectors: string (string_t*), char (char), charp (char*), voidp (void*)
*
* EXAMPLE:
* ========
* Header myvec.h:
* ---------------
// ...
VECTOR_DECLARE(myvec, my_elem_t*)
// ...
* Source myvec.c:
* ---------------
// ...
VECTOR_IMPL(myvec)
// ...
* Source main.c:
* -------------------
// ...
extern my_elem_t elems[2];
int main() {
VECTOR(myvec) *vec = vector_new_cap(myvec, 2);
vector_push(myvec, vec, &elems[0]);
vector_push(myvec, vec, &elems[1]);
vector_for (myvec, it, vec) {
printf("We have an element: %s\n", it->elem_name);
}
vector_del(myvec, vec);
}
*/
#define VECTOR(name) vector_##name##_t
#define VECTOR_ELEM(name) vector_##name##_elem
#define VECTOR_MIN_CAP 8
#define VECTOR_DECLARE_(name, t, pre) \
typedef struct vector_##name##_s { \
size_t vsize; \
size_t vcap; \
union { \
t *content; \
void *content_v; \
}; \
} VECTOR(name); \
typedef t VECTOR_ELEM(name); \
pre VECTOR_ELEM(name) *vector_steal_##name(VECTOR(name) *v); \
pre void vector_pop_del_##name(VECTOR(name) *v); \
pre void vector_popn_del_##name(VECTOR(name) *v, size_t idx); \
pre void vector_clear_del_##name(VECTOR(name) *v); \
pre void vector_del_from_##name(VECTOR(name) *v, size_t idx); \
pre int vector_push_elem_##name(VECTOR(name) *v, VECTOR_ELEM(name) elem);
#define VECTOR_DECLARE(name, t) VECTOR_DECLARE_(name, t,)
#define VECTOR_DECLARE_STATIC(name, t) VECTOR_DECLARE_(name, t, static)
VECTOR_DECLARE(voidp, void*)
VECTOR(voidp) *vector_new_impl(void);
VECTOR(voidp) *vector_new_cap_impl(size_t elem_size, size_t cap);
int vector_reserve_impl(VECTOR(voidp) *v, size_t elem_size, size_t cap);
int vector_trim_impl(VECTOR(voidp) *v, size_t elem_size);
void vector_common_pop_impl(VECTOR(voidp) *v, size_t elem_size);
void vector_common_popn_impl(VECTOR(voidp) *v, size_t n, size_t elem_size);
void vector_common_clear_impl(VECTOR(voidp) *v);
int vector_push_vec_impl(VECTOR(voidp) *v1, VECTOR(voidp) *v2, size_t start, size_t len, size_t elem_size);
#define vector_new(name) (VECTOR(name)*)vector_new_impl()
#define vector_new_cap(name, cap) (VECTOR(name)*)vector_new_cap_impl(sizeof(VECTOR_ELEM(name)), (cap))
#define vector_del(name, v) vector_del_from_##name((v), 0)
#define vector_del_freed(name, v) vector_del_from_##name((v), vector_size(name, (v)))
#define vector_del_free_from(name, v, i) vector_del_from_##name((v), (i))
#define VECTOR_DEL(name) vector_del_##name
#define vector_steal(name, v) vector_steal_##name((v))
#define vector_reserve(name, v, cap) vector_reserve_impl((VECTOR(voidp)*)(v), sizeof(VECTOR_ELEM(name)), (cap))
#define vector_trim(name, v) vector_trim_impl((VECTOR(voidp)*)(v), sizeof(VECTOR_ELEM(name)))
#define vector_push(name, v, e) vector_push_elem_##name((v), (e))
#define vector_push_vec(name, v1, v2) vector_push_vec_impl((VECTOR(voidp)*)(v1), (VECTOR(voidp)*)(v2), 0, vector_size(name, (v2)), sizeof(VECTOR_ELEM(name)))
#define vector_push_vec_slice(name, v1, v2, s, l) vector_push_vec_impl((VECTOR(voidp)*)(v1), (VECTOR(voidp)*)(v2), (s), (l), sizeof(VECTOR_ELEM(name)))
#define vector_pop(name, v) vector_pop_del_##name((v))
#define vector_pop_slice(name, v, n) vector_popn_del_##name((VECTOR(voidp)*)(v), (n))
#define vector_pop_nodel(name, v) vector_common_pop_impl((VECTOR(voidp)*)(v), sizeof(VECTOR_ELEM(name)))
#define vector_pop_nodel_slice(name, v, n) vector_common_popn_impl((VECTOR(voidp)*)(v), (n), sizeof(VECTOR_ELEM(name)))
#define vector_clear(name, v) vector_clear_del_##name((v))
#define vector_clear_nodel(name, v) vector_common_clear_impl((VECTOR(voidp)*)(v))
#define vector_remove(name, v, i) vector_remove_##name((v), (i))
#define vector_size(name, v) ((v)->vsize)
#define vector_cap(name, v) ((v)->vcap)
#define vector_content(name, v) ((v)->content)
#define vector_begin(name, v) ((v)->content)
#define vector_end(name, v) ((v)->content + (v)->vsize)
#define vector_last(name, v) ((v)->content[(v)->vsize - 1])
#define vector_for(name, itname, v) \
for (VECTOR_ELEM(name) *itname = vector_begin(name, (v)); itname < vector_end(name, (v)); ++itname)
#define vector_for_from(name, itname, v, i) \
for (VECTOR_ELEM(name) *itname = vector_begin(name, (v)) + (i); itname < vector_end(name, (v)); ++itname)
#define vector_for_rev(name, itname, v) \
for (VECTOR_ELEM(name) *itname = (v)->content ? vector_end(name, (v)) - 1 : NULL; (v)->content && (itname >= vector_begin(name, (v))); --itname)
#define VECTOR_IMPL_(name, dtor, pre) \
pre VECTOR_ELEM(name) *vector_steal_##name(VECTOR(name) *v) { \
vector_trim(name, v); \
VECTOR_ELEM(name) *ret = v->content; \
free(v); \
return ret; \
} \
\
pre void vector_pop_del_##name(VECTOR(name) *v) { \
if (v->vsize) { \
dtor(&vector_last(name, v)); \
vector_common_pop_impl((VECTOR(voidp)*)v, sizeof(VECTOR_ELEM(name))); \
} \
} \
pre void vector_popn_del_##name(VECTOR(name) *v, size_t n) { \
if (v->vsize > n) n = v->vsize; \
vector_for_from(name, it, v, v->vsize - n) { dtor(it); } \
vector_common_popn_impl((VECTOR(voidp)*)v, n, sizeof(VECTOR_ELEM(name))); \
} \
\
pre void vector_remove_##name(VECTOR(name) *v, size_t i) { \
dtor(v->content + i); \
memmove(v->content + i, v->content + i + 1, (v->vsize - i - 1) * sizeof(VECTOR_ELEM(name))); \
vector_common_pop_impl((VECTOR(voidp)*)v, sizeof(VECTOR_ELEM(name))); \
} \
\
pre void vector_clear_del_##name(VECTOR(name) *v) { \
if (!v->content) return; \
vector_for(name, it, v) { dtor(it); } \
free(v->content); \
v->content = NULL; \
v->vcap = v->vsize = 0; \
} \
\
pre void vector_del_from_##name(VECTOR(name) *v, size_t idx) { \
vector_for_from(name, it, v, idx) { dtor(it); } \
if (v->content) free(v->content); \
free(v); \
} \
\
pre int vector_push_elem_##name(VECTOR(name) *v, VECTOR_ELEM(name) elem) { \
if (v->vsize >= v->vcap) { \
size_t new_cap = (v->vcap < VECTOR_MIN_CAP) ? VECTOR_MIN_CAP : v->vcap * 2; \
VECTOR_ELEM(name) *new_content = realloc(v->content, sizeof(VECTOR_ELEM(name)) * new_cap); \
if (!new_content) return 0; \
v->content = new_content; \
v->vcap = new_cap; \
} \
v->content[v->vsize++] = elem; \
return 1; \
}
#define VECTOR_IMPL(name, dtor) VECTOR_IMPL_(name, dtor,)
#define VECTOR_IMPL_STATIC(name, dtor) VECTOR_IMPL_(name, dtor, static inline)
VECTOR_DECLARE(char, char)
VECTOR_DECLARE(charp, char*)
VECTOR_DECLARE(string, string_t*)
#endif // VECTOR_H

View File

@ -1,33 +0,0 @@
#pragma once
#include <clang/AST/ASTContext.h>
#include <clang/AST/Decl.h>
#include <clang/AST/Type.h>
#include <clang/Tooling/Tooling.h>
#include <clang/AST/RecordLayout.h>
#include <clang/AST/Decl.h>
#include <llvm/ADT/Triple.h>
#include <llvm/Support/Casting.h>
#include <cstddef>
#include <cstring>
#include <iostream>
static const clang::Type* StripTypedef(clang::QualType type) {
if (type->isTypedefNameType()) {
return StripTypedef(type->getAs<clang::TypedefType>()->getDecl()->getUnderlyingType());
} else {
return type.getTypePtr();
}
}
// FIXME: Need to support other triple except default target triple
static std::string GetDeclHeaderFile(clang::ASTContext& Ctx, clang::Decl* Decl) {
const auto& SourceManager = Ctx.getSourceManager();
const clang::FileID FileID = SourceManager.getFileID(Decl->getBeginLoc());
const clang::FileEntry *FileEntry = SourceManager.getFileEntryForID(FileID);
if (FileEntry) {
return FileEntry->getName().str();
}
return "";
}