├── CMakeLists.txt ├── EnumVisitor.cpp ├── FFIBindingsUtils.cpp ├── FunctionVisitor.cpp ├── GenerateFFIBindings.cpp ├── GenerateFFIBindings.hpp ├── LICENSE.TXT ├── Makefile ├── README.md ├── RecordVisitor.cpp ├── TypedefVisitor.cpp ├── clang-patch.diff ├── ffi-combine.lua ├── ffi-combine.sh └── ffi-gen.exports /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # If we don't need RTTI or EH, there's no reason to export anything 2 | # from the plugin. 3 | if( NOT MSVC ) # MSVC mangles symbols differently, and 4 | # ffi-gen.export contains C++ symbols. 5 | if( NOT LLVM_REQUIRES_RTTI ) 6 | if( NOT LLVM_REQUIRES_EH ) 7 | set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/ffi-gen.exports) 8 | endif() 9 | endif() 10 | endif() 11 | 12 | add_llvm_loadable_module(ffi-gen GenerateFFIBindings.cpp) 13 | 14 | if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN)) 15 | target_link_libraries(ffi-gen ${cmake_2_8_12_PRIVATE} 16 | clangAST 17 | clangBasic 18 | clangFrontend 19 | LLVMSupport 20 | ) 21 | endif() 22 | -------------------------------------------------------------------------------- /EnumVisitor.cpp: -------------------------------------------------------------------------------- 1 | #include "GenerateFFIBindings.hpp" 2 | 3 | bool EnumVisitor::VisitEnumDecl(EnumDecl *ED) { 4 | if (!ED->hasAttr()) 5 | return true; 6 | 7 | if (FFIBindingsUtils::getInstance()->isInResolvedDecls("enum " + 8 | ED->getNameAsString())) 9 | return true; 10 | 11 | if (FFIBindingsUtils::getInstance()->isOnBlacklist("enum " + 12 | ED->getNameAsString())) { 13 | FFIBindingsUtils::getInstance()->getResolvedDecls()->insert( 14 | "enum " + ED->getNameAsString()); 15 | return true; 16 | } 17 | 18 | FFIBindingsUtils::getInstance()->setHasMarkedDeclarations(true); 19 | FFIBindingsUtils::getInstance()->resolveEnumDecl(ED); 20 | 21 | return true; 22 | } 23 | -------------------------------------------------------------------------------- /FFIBindingsUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "GenerateFFIBindings.hpp" 2 | 3 | FFIBindingsUtils *FFIBindingsUtils::instance = NULL; 4 | 5 | FFIBindingsUtils *FFIBindingsUtils::getInstance() { 6 | 7 | if (!instance) 8 | instance = new FFIBindingsUtils(); 9 | 10 | return instance; 11 | } 12 | 13 | bool FFIBindingsUtils::isNewType(QualType Type) { 14 | 15 | if (isInResolvedDecls(Type.getAsString())) 16 | return false; 17 | 18 | if (isInUnresolvedDeclarations(Type.getAsString())) 19 | return false; 20 | 21 | return true; 22 | } 23 | 24 | bool FFIBindingsUtils::isInUnresolvedDeclarations(std::string DeclType) { 25 | 26 | if (UnresolvedDeclarations->find(DeclType) != UnresolvedDeclarations->end()) 27 | return true; 28 | 29 | return false; 30 | } 31 | 32 | bool FFIBindingsUtils::isInResolvedDecls(std::string DeclType) { 33 | 34 | if (ResolvedDecls->find(DeclType) != ResolvedDecls->end()) 35 | return true; 36 | 37 | return false; 38 | } 39 | 40 | bool FFIBindingsUtils::isOnBlacklist(std::string DeclType) { 41 | 42 | if (blacklist->find(DeclType) != blacklist->end()) 43 | return true; 44 | 45 | return false; 46 | } 47 | 48 | std::string FFIBindingsUtils::getArraySize(const ConstantArrayType *CAT) { 49 | 50 | std::string ArraySize = "["; 51 | 52 | if (CAT->getIndexTypeQualifiers().hasQualifiers()) { 53 | unsigned TypeQuals = CAT->getIndexTypeCVRQualifiers(); 54 | bool appendSpace = false; 55 | if (TypeQuals & Qualifiers::Const) { 56 | ArraySize += "const"; 57 | appendSpace = true; 58 | } 59 | if (TypeQuals & Qualifiers::Volatile) { 60 | if (appendSpace) 61 | ArraySize += ' '; 62 | ArraySize += "volatile"; 63 | appendSpace = true; 64 | } 65 | if (TypeQuals & Qualifiers::Restrict) { 66 | if (appendSpace) 67 | ArraySize += ' '; 68 | ArraySize += "restrict"; 69 | } 70 | ArraySize += ' '; 71 | } 72 | 73 | ArraySize += std::to_string(CAT->getSize().getZExtValue()) + ']'; 74 | return ArraySize; 75 | } 76 | 77 | std::string FFIBindingsUtils::getDeclAttrs(Decl *D) { 78 | 79 | std::string attrList; 80 | if (!D->hasAttrs()) 81 | return attrList; 82 | 83 | int numOfAttrs = 0; 84 | 85 | for (Attr *attr : D->getAttrs()) { 86 | if (attr->getKind() == attr::Kind::Packed) { 87 | if (numOfAttrs > 0) 88 | attrList = ", " + attrList; 89 | 90 | attrList = "packed" + attrList; 91 | numOfAttrs++; 92 | } else if (attr->getKind() == attr::Kind::Aligned) { 93 | if (numOfAttrs > 0) 94 | attrList = ", " + attrList; 95 | 96 | SourceLocation beginLoc = attr->getRange().getBegin(); 97 | SourceLocation endLoc = attr->getRange().getEnd(); 98 | const char *begin = 99 | D->getASTContext().getSourceManager().getCharacterData(beginLoc); 100 | const char *end = 101 | D->getASTContext().getSourceManager().getCharacterData(endLoc); 102 | attrList = std::string(begin, end + 1 - begin) + attrList; 103 | numOfAttrs++; 104 | } 105 | } 106 | 107 | if (numOfAttrs) 108 | attrList = "__attribute__((" + attrList + ")) "; 109 | return attrList; 110 | } 111 | 112 | void FFIBindingsUtils::resolveAnonRecord(RecordDecl *RD) { 113 | 114 | std::string AnonRecordName = 115 | RD->getTypeForDecl()->getCanonicalTypeInternal().getAsString(); 116 | std::string RecordDeclaration; 117 | std::vector *dependencyList = new std::vector(); 118 | bool isResolved = true; 119 | 120 | char separator; 121 | #ifdef LLVM_ON_UNIX 122 | separator = '/'; 123 | #else 124 | separator = '\\'; 125 | #endif 126 | 127 | unsigned int j = 0; 128 | int distance; 129 | for (std::vector::iterator i = getAnonymousRecords()->begin(); 130 | i != getAnonymousRecords()->end(); ++i) { 131 | if (*i == AnonRecordName) { 132 | distance = std::distance(getAnonymousRecords()->begin(), i); 133 | break; 134 | } 135 | j++; 136 | } 137 | if (j == getAnonymousRecords()->size()) { 138 | getAnonymousRecords()->push_back(AnonRecordName); 139 | distance = getAnonymousRecords()->size() - 1; 140 | } 141 | 142 | int firstindex; 143 | if (AnonRecordName.find_last_of(separator) == std::string::npos) 144 | firstindex = AnonRecordName.find("at ") + 2; 145 | else 146 | firstindex = AnonRecordName.find_last_of(separator); 147 | 148 | AnonRecordName = AnonRecordName.substr( 149 | firstindex + 1, AnonRecordName.find_first_of(':') - firstindex - 1); 150 | std::replace(AnonRecordName.begin(), AnonRecordName.end(), '.', '_'); 151 | std::replace(AnonRecordName.begin(), AnonRecordName.end(), '-', '_'); 152 | AnonRecordName = 153 | "Anonymous_" + AnonRecordName + "_" + std::to_string(distance); 154 | 155 | std::string attrs = getDeclAttrs(RD); 156 | 157 | if (RD->getTypeForDecl()->isStructureType()) { 158 | RecordDeclaration = "struct " + attrs + AnonRecordName; 159 | AnonRecordName = "struct " + AnonRecordName; 160 | } else if (RD->getTypeForDecl()->isUnionType()) { 161 | RecordDeclaration = "union " + attrs + AnonRecordName; 162 | AnonRecordName = "union " + AnonRecordName; 163 | } 164 | 165 | RecordDeclaration += " {\n"; 166 | // check the fields 167 | for (RecordDecl::field_iterator FI = RD->field_begin(); FI != RD->field_end(); 168 | ++FI) { 169 | std::string FieldDeclaration = FI->getNameAsString(); 170 | checkType(FI->getType(), &isResolved, dependencyList, FieldDeclaration, 171 | NORMAL, NONE); 172 | RecordDeclaration += FieldDeclaration + ";\n"; 173 | } 174 | RecordDeclaration += "};\n"; 175 | 176 | if (isResolved) { 177 | getResolvedDecls()->insert(AnonRecordName); 178 | (*output) += RecordDeclaration; 179 | (*output) += "\n"; 180 | delete dependencyList; 181 | } else { 182 | // add this record to list of unresolved declarations 183 | DeclarationInfo RecordDeclarationInfo; 184 | RecordDeclarationInfo.isResolved = isResolved; 185 | RecordDeclarationInfo.dependencyList = dependencyList; 186 | RecordDeclarationInfo.Declaration = RecordDeclaration; 187 | 188 | std::pair Record(AnonRecordName, 189 | RecordDeclarationInfo); 190 | getUnresolvedDeclarations()->insert(Record); 191 | } 192 | } 193 | 194 | void FFIBindingsUtils::resolveEnumDecl(EnumDecl *ED) { 195 | 196 | std::string EnumDeclaration; 197 | std::vector elements; 198 | 199 | std::string attrs = getDeclAttrs(ED); 200 | EnumDeclaration = "enum " + attrs + ED->getNameAsString() + " {"; 201 | 202 | int length = 0; 203 | for (EnumDecl::enumerator_iterator EI = ED->enumerator_begin(); 204 | EI != ED->enumerator_end(); ++EI) { 205 | SmallString<32> EnumValue(EI->getNameAsString()); 206 | if (EI->getInitExpr()) { 207 | EnumValue += " = "; 208 | EI->getInitVal().toString(EnumValue); 209 | } 210 | elements.push_back(EnumValue.str()); 211 | length++; 212 | } 213 | 214 | for (int i = 0; i < length; i++) { 215 | if (i < length - 1) 216 | EnumDeclaration += elements[i] + ", "; 217 | else 218 | EnumDeclaration += elements[i] + "};\n"; 219 | } 220 | 221 | (*output) += EnumDeclaration; // print the enumeration 222 | (*output) += "\n"; 223 | getResolvedDecls()->insert("enum " + ED->getNameAsString()); 224 | } 225 | 226 | void FFIBindingsUtils::resolveFunctionDecl(FunctionDecl *FD) { 227 | 228 | bool isResolved = true; 229 | std::string FunctionDeclaration; 230 | std::vector *dependencyList = new std::vector(); 231 | 232 | const FunctionType *FT = FD->getFunctionType(); 233 | 234 | StorageClass storageClass = FD->getStorageClass(); 235 | switch (storageClass) { 236 | case SC_Static: 237 | FunctionDeclaration = "static "; 238 | break; 239 | case SC_Extern: 240 | FunctionDeclaration = "extern "; 241 | break; 242 | default: 243 | break; 244 | } 245 | 246 | // check function return type 247 | QualType ReturnType = FT->getReturnType(); 248 | 249 | std::string ReturnValueDeclaration; 250 | checkType(ReturnType, &isResolved, dependencyList, ReturnValueDeclaration, 251 | FUNCTION, RETVAL); 252 | 253 | FunctionDeclaration += ReturnValueDeclaration + " "; 254 | FunctionDeclaration += FD->getQualifiedNameAsString() + "("; 255 | 256 | if (FD->getNumParams() == 0) 257 | FunctionDeclaration += ");\n"; 258 | else { 259 | unsigned int i = 0; 260 | // check function parameters 261 | for (ParmVarDecl *PVD : FD->params()) { 262 | QualType ParameterType = PVD->getType(); 263 | std::string Type = ParameterType.getAsString(); 264 | std::string Name = PVD->getNameAsString(); 265 | std::string ParameterDeclaration = Name; 266 | // if the parameter is (or a pointer to, or an array of) a record, 267 | // enumeration or typedef type, it should be resolved and printed before 268 | // this function's declaration 269 | checkType(ParameterType, &isResolved, dependencyList, 270 | ParameterDeclaration, FUNCTION, PARAM); 271 | FunctionDeclaration += ParameterDeclaration; 272 | 273 | if (i < FD->getNumParams() - 1) 274 | FunctionDeclaration += ", "; 275 | i++; 276 | } 277 | 278 | if (FD->isVariadic()) 279 | FunctionDeclaration += ", ..."; 280 | 281 | FunctionDeclaration += ");\n"; 282 | } 283 | 284 | if (isResolved) { 285 | getResolvedDecls()->insert("function " + FD->getQualifiedNameAsString()); 286 | (*output) += FunctionDeclaration; 287 | (*output) += "\n"; 288 | delete dependencyList; 289 | } else { 290 | DeclarationInfo FunctionDeclarationInfo; 291 | FunctionDeclarationInfo.isResolved = isResolved; 292 | FunctionDeclarationInfo.dependencyList = dependencyList; 293 | FunctionDeclarationInfo.Declaration = FunctionDeclaration; 294 | 295 | std::pair Function( 296 | "function " + FD->getQualifiedNameAsString(), FunctionDeclarationInfo); 297 | getUnresolvedDeclarations()->insert(Function); 298 | } 299 | } 300 | 301 | void FFIBindingsUtils::resolveRecordDecl(RecordDecl *RD) { 302 | 303 | bool isResolved = true; 304 | std::string RecordDeclaration; 305 | std::vector *dependencyList = new std::vector(); 306 | 307 | std::string attrList = getDeclAttrs(RD); 308 | 309 | if (RD->getTypeForDecl()->isStructureType()) 310 | RecordDeclaration = "struct "; 311 | else if (RD->getTypeForDecl()->isUnionType()) 312 | RecordDeclaration = "union "; 313 | 314 | RecordDeclaration += attrList + RD->getNameAsString() + " {\n"; 315 | 316 | // check the fields 317 | for (RecordDecl::field_iterator FI = RD->field_begin(); FI != RD->field_end(); 318 | ++FI) { 319 | if (FI->isBitField()) { 320 | RecordDeclaration += 321 | FI->getType().getAsString() + " " + FI->getNameAsString() + " : " + 322 | std::to_string(FI->getBitWidthValue(*Context)) + ";\n"; 323 | continue; 324 | } 325 | std::string FieldDeclaration = FI->getNameAsString(); 326 | checkType(FI->getType(), &isResolved, dependencyList, FieldDeclaration, 327 | NORMAL, NONE); 328 | RecordDeclaration += FieldDeclaration + ";\n"; 329 | } 330 | RecordDeclaration += "};\n"; 331 | if (isResolved) { 332 | getResolvedDecls()->insert( 333 | RD->getTypeForDecl()->getCanonicalTypeInternal().getAsString()); 334 | if (RD->field_empty()) 335 | (*output) += 336 | RD->getTypeForDecl()->getCanonicalTypeInternal().getAsString() + 337 | ";\n"; 338 | else 339 | (*output) += RecordDeclaration; 340 | (*output) += "\n"; 341 | 342 | delete dependencyList; 343 | } else { 344 | // add this record to list of unresolved declarations 345 | DeclarationInfo RecordDeclarationInfo; 346 | RecordDeclarationInfo.isResolved = isResolved; 347 | RecordDeclarationInfo.dependencyList = dependencyList; 348 | RecordDeclarationInfo.Declaration = RecordDeclaration; 349 | 350 | std::pair Record( 351 | RD->getTypeForDecl()->getCanonicalTypeInternal().getAsString(), 352 | RecordDeclarationInfo); 353 | getUnresolvedDeclarations()->insert(Record); 354 | } 355 | } 356 | 357 | void FFIBindingsUtils::resolveTypedefDecl(TypedefNameDecl *TD) { 358 | 359 | std::string TypedefDeclaration = "typedef "; 360 | 361 | QualType UnderlyingTypeFull = TD->getUnderlyingType(); 362 | QualType UnderlyingType = TD->getUnderlyingType(); 363 | unsigned Qualifiers = UnderlyingType.getLocalCVRQualifiers(); 364 | UnderlyingType.removeLocalCVRQualifiers(Qualifiers); 365 | 366 | if (const TypedefType *TT = UnderlyingType->getAs()) { 367 | 368 | TypedefNameDecl *typedefDecl = TT->getDecl(); 369 | std::string TypedefDeclaration = "typedef " + 370 | UnderlyingTypeFull.getAsString() + " " + 371 | TD->getNameAsString() + ";\n"; 372 | 373 | if (isOnBlacklist(UnderlyingType.getAsString())) 374 | getResolvedDecls()->insert("typedef " + TD->getNameAsString()); 375 | else { 376 | bool isResolved = false; 377 | std::vector *dependencyList = new std::vector(); 378 | 379 | TypeDeclaration TypedefTypeDeclaration; 380 | TypedefTypeDeclaration.Declaration = typedefDecl; 381 | TypedefTypeDeclaration.TypeName = 382 | "typedef " + UnderlyingType.getAsString(); 383 | dependencyList->push_back("typedef " + UnderlyingType.getAsString()); 384 | 385 | DeclarationInfo TypedefDeclarationInfo; 386 | TypedefDeclarationInfo.isResolved = isResolved; 387 | TypedefDeclarationInfo.dependencyList = dependencyList; 388 | TypedefDeclarationInfo.Declaration = TypedefDeclaration; 389 | 390 | std::pair TypedefDecl( 391 | "typedef " + TD->getNameAsString(), TypedefDeclarationInfo); 392 | 393 | getUnresolvedDeclarations()->insert(TypedefDecl); 394 | 395 | if (isNewType(UnderlyingType)) 396 | getDeclsToFind()->push(TypedefTypeDeclaration); 397 | } 398 | 399 | } else if (UnderlyingType->isFundamentalType()) { 400 | 401 | TypedefDeclaration += 402 | UnderlyingTypeFull.getAsString() + " " + TD->getNameAsString() + ";\n"; 403 | getResolvedDecls()->insert("typedef " + TD->getNameAsString()); 404 | (*output) += TypedefDeclaration; 405 | (*output) += "\n"; 406 | 407 | } else if (UnderlyingType->isRecordType()) { 408 | 409 | bool isResolved = false; 410 | std::vector *dependencyList = new std::vector(); 411 | 412 | const RecordType *RT = UnderlyingType->getAs(); 413 | RecordDecl *recordDecl = RT->getDecl(); 414 | 415 | if (isOnBlacklist(UnderlyingType.getAsString())) { 416 | getResolvedDecls()->insert(recordDecl->getTypeForDecl() 417 | ->getCanonicalTypeInternal() 418 | .getAsString()); 419 | TypedefDeclaration += UnderlyingTypeFull.getAsString() + " "; 420 | } else { 421 | if (recordDecl->getNameAsString() == "") { 422 | std::string AnonRecordDeclaration; 423 | 424 | if (UnderlyingType->isStructureType()) 425 | TypedefDeclaration += "struct "; 426 | else if (UnderlyingType->isUnionType()) 427 | TypedefDeclaration += "union "; 428 | 429 | std::string attrList = getDeclAttrs(recordDecl); 430 | TypedefDeclaration += attrList + "{\n"; 431 | 432 | for (RecordDecl::field_iterator FI = recordDecl->field_begin(); 433 | FI != recordDecl->field_end(); ++FI) { 434 | std::string FieldDeclaration = FI->getNameAsString(); 435 | checkType(FI->getType(), &isResolved, dependencyList, 436 | FieldDeclaration, NORMAL, NONE); 437 | AnonRecordDeclaration += FieldDeclaration + ";\n"; 438 | } 439 | 440 | TypedefDeclaration += AnonRecordDeclaration + "} "; 441 | 442 | } else { 443 | TypedefDeclaration += UnderlyingTypeFull.getAsString() + " "; 444 | dependencyList->push_back(recordDecl->getTypeForDecl() 445 | ->getCanonicalTypeInternal() 446 | .getAsString()); 447 | 448 | if (isNewType(UnderlyingType)) { 449 | TypeDeclaration RecordTypeDeclaration; 450 | RecordTypeDeclaration.Declaration = recordDecl; 451 | RecordTypeDeclaration.TypeName = recordDecl->getTypeForDecl() 452 | ->getCanonicalTypeInternal() 453 | .getAsString(); 454 | getDeclsToFind()->push(RecordTypeDeclaration); 455 | } 456 | } 457 | } 458 | 459 | TypedefDeclaration += TD->getNameAsString() + ";\n"; 460 | 461 | DeclarationInfo TypedefDeclarationInfo; 462 | TypedefDeclarationInfo.isResolved = isResolved; 463 | TypedefDeclarationInfo.dependencyList = dependencyList; 464 | TypedefDeclarationInfo.Declaration = TypedefDeclaration; 465 | 466 | std::pair TypedefDecl( 467 | "typedef " + TD->getNameAsString(), TypedefDeclarationInfo); 468 | 469 | getUnresolvedDeclarations()->insert(TypedefDecl); 470 | 471 | } else if (UnderlyingType->isEnumeralType()) { 472 | 473 | const EnumType *ET = UnderlyingType->castAs(); 474 | EnumDecl *enumDecl = ET->getDecl(); 475 | std::string TypedefDeclaration = "typedef enum "; 476 | 477 | if (isOnBlacklist(UnderlyingType.getAsString())) { 478 | getResolvedDecls()->insert(UnderlyingType.getAsString()); 479 | TypedefDeclaration += enumDecl->getNameAsString() + " "; 480 | } else { 481 | 482 | std::vector elements; 483 | 484 | TypedefDeclaration += getDeclAttrs(enumDecl); 485 | 486 | if (enumDecl->getNameAsString() != "") { 487 | TypedefDeclaration += enumDecl->getNameAsString() + " "; 488 | } 489 | TypedefDeclaration += "{"; 490 | 491 | int length = 0; 492 | for (EnumDecl::enumerator_iterator EI = enumDecl->enumerator_begin(); 493 | EI != enumDecl->enumerator_end(); ++EI) { 494 | SmallString<32> EnumValue(EI->getNameAsString()); 495 | if (EI->getInitExpr()) { 496 | EnumValue += " = "; 497 | EI->getInitVal().toString(EnumValue); 498 | } 499 | elements.push_back(EnumValue.str()); 500 | length++; 501 | } 502 | 503 | for (int i = 0; i < length; i++) { 504 | if (i < length - 1) { 505 | TypedefDeclaration += elements[i] + ", "; 506 | } else { 507 | TypedefDeclaration += elements[i] + "} "; 508 | } 509 | } 510 | } 511 | TypedefDeclaration += TD->getNameAsString() + ";\n"; 512 | (*output) += TypedefDeclaration; // print the typedef 513 | (*output) += "\n"; 514 | 515 | getResolvedDecls()->insert("typedef " + TD->getNameAsString()); 516 | 517 | } else if (UnderlyingType->isFunctionPointerType()) { 518 | 519 | std::string FunctionPointerDeclarationCore; 520 | bool isResolved = false; 521 | std::vector *dependencyList = new std::vector(); 522 | const FunctionProtoType *FPT = 523 | (const FunctionProtoType *) 524 | UnderlyingType->getPointeeType()->getAs(); 525 | std::string ReturnValueDeclaration; 526 | 527 | checkType(FPT->getReturnType(), &isResolved, dependencyList, 528 | ReturnValueDeclaration, FUNCTION, RETVAL); 529 | std::string TypedefDeclaration = "typedef " + ReturnValueDeclaration + 530 | " (*" + TD->getNameAsString() + ")" + "("; 531 | 532 | unsigned int NumOfParams = FPT->getNumParams(); 533 | for (unsigned int i = 0; i < NumOfParams; i++) { 534 | std::string ParameterDeclaration; 535 | checkType(FPT->getParamType(i), &isResolved, dependencyList, 536 | ParameterDeclaration, FUNCTION, PARAM); 537 | FunctionPointerDeclarationCore += ParameterDeclaration; 538 | if (i != NumOfParams - 1) 539 | FunctionPointerDeclarationCore += ", "; 540 | } 541 | 542 | if (FPT->isVariadic()) 543 | FunctionPointerDeclarationCore += ", ..."; 544 | 545 | FunctionPointerDeclarationCore += ");\n"; 546 | TypedefDeclaration += FunctionPointerDeclarationCore; 547 | 548 | DeclarationInfo TypedefDeclarationInfo; 549 | TypedefDeclarationInfo.isResolved = isResolved; 550 | TypedefDeclarationInfo.dependencyList = dependencyList; 551 | TypedefDeclarationInfo.Declaration = TypedefDeclaration; 552 | 553 | std::pair TypedefDecl( 554 | "typedef " + TD->getNameAsString(), TypedefDeclarationInfo); 555 | 556 | getUnresolvedDeclarations()->insert(TypedefDecl); 557 | 558 | } else if (UnderlyingType->isPointerType()) { 559 | 560 | bool isResolved = false; 561 | std::vector *dependencyList = new std::vector(); 562 | std::string TypedefDeclaration = "typedef "; 563 | std::string ElementType; 564 | 565 | if (UnderlyingType->getPointeeType()->isFundamentalType() && 566 | !(UnderlyingType->getPointeeType()->getAs())) { 567 | TypedefDeclaration += UnderlyingTypeFull.getAsString() + " " + 568 | TD->getNameAsString() + ";\n"; 569 | isResolved = true; 570 | } else { 571 | std::string DeclarationCore = "(*" + TD->getNameAsString() + ")"; 572 | checkType(UnderlyingType->getPointeeType(), &isResolved, dependencyList, 573 | DeclarationCore, NORMAL, NONE); 574 | 575 | TypedefDeclaration += DeclarationCore + ";\n"; 576 | isResolved = false; 577 | 578 | DeclarationInfo TypedefDeclarationInfo; 579 | TypedefDeclarationInfo.isResolved = isResolved; 580 | TypedefDeclarationInfo.dependencyList = dependencyList; 581 | TypedefDeclarationInfo.Declaration = TypedefDeclaration; 582 | 583 | std::pair TypedefDecl( 584 | "typedef " + TD->getNameAsString(), TypedefDeclarationInfo); 585 | 586 | getUnresolvedDeclarations()->insert(TypedefDecl); 587 | } 588 | if (isResolved) { 589 | getResolvedDecls()->insert("typedef " + TD->getNameAsString()); 590 | (*output) += TypedefDeclaration; // print the typedef 591 | (*output) += "\n"; 592 | 593 | delete dependencyList; 594 | } 595 | } else if (UnderlyingType->isArrayType()) { 596 | 597 | std::string TypedefDeclaration = "typedef "; 598 | 599 | bool isResolved = false; 600 | std::vector *dependencyList = new std::vector(); 601 | std::string DeclarationCore = TD->getNameAsString(); 602 | 603 | std::string ArrayDeclaration; 604 | QualType ElementType; 605 | 606 | if (UnderlyingType->isConstantArrayType()) { 607 | ArrayDeclaration = 608 | getArraySize(Context->getAsConstantArrayType(UnderlyingType)); 609 | ElementType = 610 | Context->getAsConstantArrayType(UnderlyingType)->getElementType(); 611 | } else if (UnderlyingType->isIncompleteArrayType()) { 612 | ArrayDeclaration = "[]"; 613 | ElementType = 614 | Context->getAsIncompleteArrayType(UnderlyingType)->getElementType(); 615 | } 616 | 617 | DeclarationCore += ArrayDeclaration; 618 | checkType(ElementType, &isResolved, dependencyList, DeclarationCore, NORMAL, 619 | NONE); 620 | 621 | TypedefDeclaration += DeclarationCore + ";\n"; 622 | 623 | DeclarationInfo TypedefDeclarationInfo; 624 | TypedefDeclarationInfo.isResolved = isResolved; 625 | TypedefDeclarationInfo.dependencyList = dependencyList; 626 | TypedefDeclarationInfo.Declaration = TypedefDeclaration; 627 | 628 | std::pair TypedefDecl( 629 | "typedef " + TD->getNameAsString(), TypedefDeclarationInfo); 630 | 631 | getUnresolvedDeclarations()->insert(TypedefDecl); 632 | 633 | } else if (const VectorType *VT = UnderlyingType->getAs()) { 634 | 635 | if (VT->getVectorKind() == VectorType::GenericVector && 636 | VT->getElementType()->isFundamentalType()) { 637 | TypedefDeclaration += 638 | VT->getElementType().getAsString() + " " + TD->getNameAsString() + 639 | " __attribute__((__vector_size__(" + 640 | std::to_string(VT->getNumElements()) + " * sizeof(" + 641 | VT->getElementType().getAsString() + "))));\n"; 642 | getResolvedDecls()->insert("typedef " + TD->getNameAsString()); 643 | (*output) += TypedefDeclaration; 644 | (*output) += "\n"; 645 | } 646 | } 647 | } 648 | 649 | void FFIBindingsUtils::checkType(QualType ParameterType, bool *isResolved, 650 | std::vector *dependencyList, 651 | std::string &DeclarationCore, 652 | enum ParentDeclType type, 653 | enum ParamType parameterType) { 654 | 655 | unsigned Qualifiers = ParameterType.getLocalCVRQualifiers(); 656 | QualType ParamTypeFull = ParameterType; 657 | ParameterType.removeLocalCVRQualifiers(Qualifiers); 658 | 659 | // typedef type should be checked for first, because e.g. a typedef of a 660 | // structure is both typedef type and record type 661 | if (const TypedefType *TT = ParameterType->getAs()) { 662 | 663 | if (isOnBlacklist(ParameterType.getAsString())) 664 | getResolvedDecls()->insert("typedef " + ParameterType.getAsString()); 665 | else { 666 | TypeDeclaration TypedefTypeDeclaration; 667 | TypedefTypeDeclaration.Declaration = TT->getDecl(); 668 | TypedefTypeDeclaration.TypeName = 669 | "typedef " + ParameterType.getAsString(); 670 | 671 | *isResolved = false; 672 | dependencyList->push_back("typedef " + ParameterType.getAsString()); 673 | 674 | if (isNewType(ParameterType)) 675 | getDeclsToFind()->push(TypedefTypeDeclaration); 676 | } 677 | if (DeclarationCore == "") 678 | DeclarationCore = ParamTypeFull.getAsString(); 679 | else 680 | DeclarationCore = ParamTypeFull.getAsString() + " " + DeclarationCore; 681 | 682 | } else if (const RecordType *RT = ParameterType->getAs()) { 683 | 684 | if (isOnBlacklist(ParameterType.getAsString())) { 685 | getResolvedDecls()->insert(ParameterType.getAsString()); 686 | if (DeclarationCore == "") 687 | DeclarationCore = ParamTypeFull.getAsString(); 688 | else 689 | DeclarationCore = ParamTypeFull.getAsString() + " " + DeclarationCore; 690 | } else { 691 | 692 | RecordDecl *RD = RT->getDecl(); 693 | TypeDeclaration RecordTypeDeclaration; 694 | RecordTypeDeclaration.Declaration = RD; 695 | 696 | if (RD->getNameAsString() == "") { 697 | 698 | if (type == FUNCTION) { 699 | std::string AnonRecordName = 700 | RD->getTypeForDecl()->getCanonicalTypeInternal().getAsString(); 701 | char separator; 702 | #ifdef LLVM_ON_UNIX 703 | separator = '/'; 704 | #else 705 | separator = '\\'; 706 | #endif 707 | 708 | unsigned int j = 0; 709 | int distance; 710 | for (std::vector::iterator i = 711 | getAnonymousRecords()->begin(); 712 | i != getAnonymousRecords()->end(); ++i) { 713 | if (*i == AnonRecordName) { 714 | distance = std::distance(getAnonymousRecords()->begin(), i); 715 | break; 716 | } 717 | j++; 718 | } 719 | if (j == getAnonymousRecords()->size()) { 720 | getAnonymousRecords()->push_back(AnonRecordName); 721 | distance = getAnonymousRecords()->size() - 1; 722 | } 723 | 724 | int firstindex; 725 | if (AnonRecordName.find_last_of(separator) == std::string::npos) { 726 | firstindex = AnonRecordName.find("at ") + 2; 727 | } else { 728 | firstindex = AnonRecordName.find_last_of(separator); 729 | } 730 | 731 | AnonRecordName = AnonRecordName.substr( 732 | firstindex + 1, 733 | AnonRecordName.find_first_of(':') - firstindex - 1); 734 | std::replace(AnonRecordName.begin(), AnonRecordName.end(), '.', '_'); 735 | std::replace(AnonRecordName.begin(), AnonRecordName.end(), '-', '_'); 736 | AnonRecordName = 737 | "Anonymous_" + AnonRecordName + "_" + std::to_string(distance); 738 | 739 | if (RT->isStructureType()) 740 | AnonRecordName = "struct " + AnonRecordName; 741 | else if (RT->isUnionType()) 742 | AnonRecordName = "union " + AnonRecordName; 743 | 744 | *isResolved = false; 745 | 746 | RecordTypeDeclaration.TypeName = AnonRecordName; 747 | dependencyList->push_back(AnonRecordName); 748 | 749 | if (!isInResolvedDecls(AnonRecordName) && 750 | !isInUnresolvedDeclarations(AnonRecordName)) 751 | getDeclsToFind()->push(RecordTypeDeclaration); 752 | 753 | if (DeclarationCore == "") 754 | DeclarationCore = AnonRecordName; 755 | else 756 | DeclarationCore = AnonRecordName + " " + DeclarationCore; 757 | 758 | } else if (type == NORMAL) { 759 | std::string AnonRecordDeclaration; 760 | 761 | if (RT->isStructureType()) 762 | AnonRecordDeclaration += "struct "; 763 | else if (RT->isUnionType()) 764 | AnonRecordDeclaration += "union "; 765 | 766 | AnonRecordDeclaration += getDeclAttrs(RD); 767 | AnonRecordDeclaration += "{\n"; 768 | 769 | for (RecordDecl::field_iterator FI = RD->field_begin(); 770 | FI != RD->field_end(); ++FI) { 771 | std::string FieldDeclaration = FI->getNameAsString(); 772 | checkType(FI->getType(), isResolved, dependencyList, 773 | FieldDeclaration, NORMAL, NONE); 774 | AnonRecordDeclaration += FieldDeclaration + ";\n"; 775 | } 776 | 777 | AnonRecordDeclaration += "}"; 778 | 779 | if (!RD->isAnonymousStructOrUnion()) 780 | AnonRecordDeclaration += " "; 781 | 782 | DeclarationCore = AnonRecordDeclaration + DeclarationCore; 783 | } 784 | 785 | } else { 786 | RecordTypeDeclaration.TypeName = ParameterType.getAsString(); 787 | 788 | *isResolved = false; 789 | dependencyList->push_back(ParameterType.getAsString()); 790 | 791 | if (isNewType(ParameterType)) 792 | getDeclsToFind()->push(RecordTypeDeclaration); 793 | 794 | if (DeclarationCore == "") 795 | DeclarationCore = ParamTypeFull.getAsString(); 796 | else 797 | DeclarationCore = ParamTypeFull.getAsString() + " " + DeclarationCore; 798 | } 799 | } 800 | 801 | } else if (const EnumType *ET = ParameterType->getAs()) { 802 | 803 | if (isOnBlacklist(ParameterType.getAsString())) { 804 | getResolvedDecls()->insert(ParameterType.getAsString()); 805 | if (DeclarationCore == "") 806 | DeclarationCore = ParamTypeFull.getAsString(); 807 | else 808 | DeclarationCore = ParamTypeFull.getAsString() + " " + DeclarationCore; 809 | } else { 810 | EnumDecl *ED = ET->getDecl(); 811 | 812 | if (ED->getNameAsString() == "") { 813 | std::vector elements; 814 | std::string AnonEnumDeclaration; 815 | 816 | std::string attrs = getDeclAttrs(ED); 817 | AnonEnumDeclaration += "enum " + attrs + "{"; 818 | 819 | int length = 0; 820 | for (EnumDecl::enumerator_iterator EI = ED->enumerator_begin(); 821 | EI != ED->enumerator_end(); ++EI) { 822 | SmallString<32> EnumValue(EI->getNameAsString()); 823 | if (EI->getInitExpr()) { 824 | EnumValue += " = "; 825 | EI->getInitVal().toString(EnumValue); 826 | } 827 | elements.push_back(EnumValue.str()); 828 | length++; 829 | } 830 | 831 | for (int i = 0; i < length; i++) { 832 | if (i < length - 1) 833 | AnonEnumDeclaration += elements[i] + ", "; 834 | else { 835 | if (type == FUNCTION) 836 | AnonEnumDeclaration += elements[i] + "}"; 837 | else if (type == NORMAL) 838 | AnonEnumDeclaration += elements[i] + "} "; 839 | } 840 | } 841 | DeclarationCore = AnonEnumDeclaration + DeclarationCore; 842 | } else { 843 | TypeDeclaration EnumTypeDeclaration; 844 | EnumTypeDeclaration.Declaration = ED; 845 | EnumTypeDeclaration.TypeName = ParameterType.getAsString(); 846 | 847 | *isResolved = false; 848 | dependencyList->push_back(ParameterType.getAsString()); 849 | 850 | if (isNewType(ParameterType)) 851 | getDeclsToFind()->push(EnumTypeDeclaration); 852 | 853 | if (DeclarationCore == "") 854 | DeclarationCore = ParamTypeFull.getAsString(); 855 | else 856 | DeclarationCore = ParamTypeFull.getAsString() + " " + DeclarationCore; 857 | } 858 | } 859 | 860 | } else if (ParameterType->isFunctionPointerType()) { 861 | 862 | std::string FunctionPointerDeclarationCore; 863 | const FunctionProtoType *FPT = 864 | (const FunctionProtoType *) 865 | ParameterType->getPointeeType()->getAs(); 866 | std::string ReturnValueDeclaration; 867 | 868 | checkType(FPT->getReturnType(), isResolved, dependencyList, 869 | ReturnValueDeclaration, FUNCTION, RETVAL); 870 | DeclarationCore = 871 | ReturnValueDeclaration + " (*" + DeclarationCore + ")" + "("; 872 | 873 | unsigned int NumOfParams = FPT->getNumParams(); 874 | for (unsigned int i = 0; i < NumOfParams; i++) { 875 | std::string ParameterDeclaration; 876 | checkType(FPT->getParamType(i), isResolved, dependencyList, 877 | ParameterDeclaration, FUNCTION, PARAM); 878 | FunctionPointerDeclarationCore += ParameterDeclaration; 879 | if (i != NumOfParams - 1) 880 | FunctionPointerDeclarationCore += ", "; 881 | } 882 | 883 | if (FPT->isVariadic()) 884 | FunctionPointerDeclarationCore += ", ..."; 885 | 886 | FunctionPointerDeclarationCore += ")"; 887 | DeclarationCore += FunctionPointerDeclarationCore; 888 | 889 | } else if (ParameterType->isPointerType()) { 890 | 891 | if (type == FUNCTION) { 892 | if (parameterType == RETVAL) { 893 | checkType(ParameterType->getPointeeType(), isResolved, dependencyList, 894 | DeclarationCore, type, parameterType); 895 | DeclarationCore += "*"; 896 | } else { 897 | DeclarationCore = "(*" + DeclarationCore + ")"; 898 | checkType(ParameterType->getPointeeType(), isResolved, dependencyList, 899 | DeclarationCore, type, parameterType); 900 | } 901 | } else if (type == NORMAL) { 902 | DeclarationCore = "(*" + DeclarationCore + ")"; 903 | checkType(ParameterType->getPointeeType(), isResolved, dependencyList, 904 | DeclarationCore, type, parameterType); 905 | } 906 | } else if (ParameterType->isArrayType()) { 907 | std::string ArrayDeclaration; 908 | QualType ElementType; 909 | 910 | if (ParameterType->isConstantArrayType()) { 911 | ArrayDeclaration = 912 | getArraySize(Context->getAsConstantArrayType(ParameterType)); 913 | ElementType = 914 | Context->getAsConstantArrayType(ParameterType)->getElementType(); 915 | } else if (ParameterType->isIncompleteArrayType()) { 916 | ArrayDeclaration = "[]"; 917 | ElementType = 918 | Context->getAsIncompleteArrayType(ParameterType)->getElementType(); 919 | } 920 | 921 | DeclarationCore += ArrayDeclaration; 922 | checkType(ElementType, isResolved, dependencyList, DeclarationCore, type, 923 | parameterType); 924 | } else if (ParameterType->isFundamentalType()) { 925 | 926 | if (DeclarationCore == "") 927 | DeclarationCore = ParamTypeFull.getAsString(); 928 | else 929 | DeclarationCore = ParamTypeFull.getAsString() + " " + DeclarationCore; 930 | 931 | } else if (const VectorType *VT = ParameterType->getAs()) { 932 | 933 | if (VT->getVectorKind() == VectorType::GenericVector && 934 | VT->getElementType()->isFundamentalType()) 935 | DeclarationCore = VT->getElementType().getAsString() + " " + 936 | DeclarationCore + " __attribute__((__vector_size__(" + 937 | std::to_string(VT->getNumElements()) + " * sizeof(" + 938 | VT->getElementType().getAsString() + "))))"; 939 | } 940 | } 941 | -------------------------------------------------------------------------------- /FunctionVisitor.cpp: -------------------------------------------------------------------------------- 1 | #include "GenerateFFIBindings.hpp" 2 | 3 | bool FunctionVisitor::VisitFunctionDecl(FunctionDecl *FD) { 4 | 5 | if (!FFIBindingsUtils::getInstance()->isTestingModeOn()) { 6 | if (!FD->hasAttr()) 7 | return true; 8 | } 9 | 10 | if (FFIBindingsUtils::getInstance()->isInResolvedDecls( 11 | "function " + FD->getQualifiedNameAsString()) || 12 | FFIBindingsUtils::getInstance()->isInUnresolvedDeclarations( 13 | "function " + FD->getQualifiedNameAsString())) 14 | return true; 15 | 16 | FFIBindingsUtils::getInstance()->setHasMarkedDeclarations(true); 17 | FFIBindingsUtils::getInstance()->resolveFunctionDecl(FD); 18 | 19 | return true; 20 | } 21 | -------------------------------------------------------------------------------- /GenerateFFIBindings.cpp: -------------------------------------------------------------------------------- 1 | //===- GenerateFFIBindings.cpp 2 | //---------------------------------------------===// 3 | // 4 | // The LLVM Compiler Infrastructure 5 | // 6 | // This file is distributed under the University of Illinois Open Source 7 | // License. See LICENSE.TXT for details. 8 | // 9 | //===----------------------------------------------------------------------===// 10 | // 11 | // Clang plugin which generates LuaJIT ffi bindings for functions, 12 | // records (structs and unions) and enums in the input file that are marked with 13 | // the ffi binding attribute. 14 | // 15 | //===----------------------------------------------------------------------===// 16 | 17 | #include "GenerateFFIBindings.hpp" 18 | /** 19 | * Class used for generating required FFI bindings by traversing through 20 | * the parsed AST and extracting relevant information from the 21 | * appropriate nodes. 22 | * 23 | * Implementation of the ASTConsumer interface. 24 | * It implements HandleTranslationUnit method, which gets called when 25 | * the AST for entire translation unit has been parsed. 26 | * 27 | **/ 28 | class GenerateFFIBindingsConsumer : public ASTConsumer { 29 | public: 30 | GenerateFFIBindingsConsumer() {} 31 | virtual void HandleTranslationUnit(clang::ASTContext &context) { 32 | 33 | DiagnosticsEngine &DE = context.getDiagnostics(); 34 | if (DE.hasErrorOccurred()) { 35 | llvm::outs() << "----------------------------------\n"; 36 | llvm::outs() << "--------- ffi-gen plugin ---------\n"; 37 | llvm::outs() << "----------------------------------\n"; 38 | llvm::outs() << "Error has occurred during compilation "; 39 | llvm::outs() << "- ffi bindings will not be generated.\n\n"; 40 | return; 41 | } 42 | 43 | std::error_code Err; 44 | utils = FFIBindingsUtils::getInstance(); 45 | 46 | std::string outputFileName = utils->getOutputFileName(); 47 | std::string headerFileName = utils->getHeaderFileName(); 48 | std::string blacklistFileName = utils->getBlacklistFileName(); 49 | 50 | std::string sourceFileName = 51 | context.getSourceManager() 52 | .getFileEntryForID(context.getSourceManager().getMainFileID()) 53 | ->getName(); 54 | std::string dirName = 55 | context.getSourceManager().getFileManager().getCanonicalName( 56 | context.getSourceManager() 57 | .getFileEntryForID(context.getSourceManager().getMainFileID()) 58 | ->getDir()); 59 | 60 | char separator; 61 | #ifdef LLVM_ON_UNIX 62 | separator = '/'; 63 | #else 64 | separator = '\\'; 65 | #endif 66 | dirName += separator; 67 | if (sourceFileName.find_last_of(separator) != std::string::npos) 68 | sourceFileName = 69 | sourceFileName.substr(sourceFileName.find_last_of(separator) + 1); 70 | 71 | if (utils->isTestingModeOn()) { 72 | outputFileName = sourceFileName; 73 | outputFileName.replace( 74 | outputFileName.find_first_of('.'), 75 | outputFileName.length() - outputFileName.find_first_of('.'), ""); 76 | outputFileName = dirName + outputFileName; 77 | std::replace(outputFileName.begin(), outputFileName.end(), separator, 78 | '_'); 79 | 80 | outputFileName += ".lua"; 81 | } 82 | 83 | sourceFileName = ">> " + dirName + sourceFileName; 84 | 85 | if (headerFileName != "") { 86 | std::string line; 87 | std::ifstream headerFile(headerFileName); 88 | if (headerFile.is_open()) { 89 | while (getline(headerFile, line)) { 90 | if (line.find(constants::SRC_FILE_PLACE_HOLDER) != std::string::npos) 91 | line.replace(line.find(constants::SRC_FILE_PLACE_HOLDER), 92 | constants::SRC_FILE_PLACE_HOLDER.length(), 93 | sourceFileName); 94 | output += line + "\n"; 95 | } 96 | headerFile.close(); 97 | } else { 98 | llvm::outs() << "Error opening file: \"" << headerFileName << "\"\n"; 99 | return; 100 | } 101 | } 102 | 103 | if (blacklistFileName != "") { 104 | std::string line; 105 | std::ifstream blacklistFile(blacklistFileName); 106 | if (blacklistFile.is_open()) { 107 | while (getline(blacklistFile, line)) { 108 | utils->getBlacklist()->insert(line); 109 | } 110 | blacklistFile.close(); 111 | } else { 112 | llvm::outs() << "Error opening file: \"" << blacklistFileName << "\"\n"; 113 | return; 114 | } 115 | } 116 | 117 | output += "ffi = require(\"ffi\")\nffi.cdef[[\n\n"; 118 | 119 | FFIBindingsUtils::getInstance()->setOutput(&output); 120 | FFIBindingsUtils::getInstance()->setContext(&context); 121 | 122 | // visit all function declarations and extract information 123 | // about unresolved dependencies, if there are any 124 | FunctionsVisitor.TraverseDecl(context.getTranslationUnitDecl()); 125 | // visit all record declarations that are marked with ffibinding attribute 126 | // and start resolving them 127 | RecordsVisitor.TraverseDecl(context.getTranslationUnitDecl()); 128 | // visit all enum declarations that are marked with ffibinding attribute 129 | // and print them out 130 | EnumsVisitor.TraverseDecl(context.getTranslationUnitDecl()); 131 | // visit all typedef declarations that are marked with ffibinding attribute 132 | // and start resolving them 133 | TypedefsVisitor.TraverseDecl(context.getTranslationUnitDecl()); 134 | 135 | // go through DeclsToFind until all required declarations are found 136 | while (utils->getDeclsToFind()->size() > 0) { 137 | 138 | TypeDeclaration Decl = utils->getDeclsToFind()->top(); 139 | utils->getDeclsToFind()->pop(); 140 | 141 | const Type *DeclType = Decl.Declaration->getTypeForDecl(); 142 | 143 | if (!utils->isInResolvedDecls(Decl.TypeName) && 144 | !utils->isInUnresolvedDeclarations(Decl.TypeName)) { 145 | if (DeclType->getAs()) 146 | FFIBindingsUtils::getInstance()->resolveTypedefDecl( 147 | (TypedefNameDecl *)Decl.Declaration); 148 | else if (DeclType->isRecordType()) { 149 | if (Decl.Declaration->getNameAsString() == "") 150 | FFIBindingsUtils::getInstance()->resolveAnonRecord( 151 | (RecordDecl *)Decl.Declaration); 152 | else 153 | FFIBindingsUtils::getInstance()->resolveRecordDecl( 154 | (RecordDecl *)Decl.Declaration); 155 | } else if (DeclType->isEnumeralType()) 156 | FFIBindingsUtils::getInstance()->resolveEnumDecl( 157 | (EnumDecl *)Decl.Declaration); 158 | } 159 | } 160 | 161 | unsigned int i = 0; 162 | bool possibleLoop = false; 163 | 164 | while (i < utils->getUnresolvedDeclarations()->size()) { 165 | i = 0; 166 | bool printedSomething = false; 167 | // iterate through the list of unresolved declarations until 168 | // they are all printed (become resolved); 169 | // declaration printing must be done in a certain order 170 | for (std::map::iterator it = 171 | utils->getUnresolvedDeclarations()->begin(); 172 | it != utils->getUnresolvedDeclarations()->end(); ++it) { 173 | std::pair Decl = *it; 174 | std::string DeclName = Decl.first; 175 | DeclarationInfo DeclInfo = Decl.second; 176 | if (DeclInfo.isResolved) { 177 | i++; // increment the number of already resolved declarations 178 | continue; // continue to the next one in the list 179 | } 180 | unsigned int j = 181 | 0; // a counter used for checking if all dependencies are resolved 182 | 183 | for (std::vector::iterator it1 = 184 | DeclInfo.dependencyList->begin(); 185 | it1 != DeclInfo.dependencyList->end(); ++it1) { 186 | // check if this dependency is resolved 187 | if (utils->isInResolvedDecls(*it1)) 188 | j++; 189 | else 190 | break; 191 | } 192 | if (j == DeclInfo.dependencyList->size()) { // if all dependencies of 193 | // this declaration are 194 | // resolved, it can be 195 | // printed out and marked as 196 | // resolved 197 | (*it).second.isResolved = true; 198 | utils->getResolvedDecls()->insert(DeclName); 199 | output += DeclInfo.Declaration; // print out the declaration 200 | output += "\n"; 201 | printedSomething = true; 202 | possibleLoop = false; 203 | delete DeclInfo.dependencyList; 204 | } else { 205 | if (possibleLoop && !utils->isInResolvedDecls(DeclName) && 206 | DependsOnItself(DeclName, DeclInfo.dependencyList)) { 207 | utils->getResolvedDecls()->insert(DeclName); 208 | output += DeclName; // print out the forward declaration 209 | output += ";\n"; 210 | printedSomething = true; 211 | possibleLoop = false; 212 | } 213 | continue; // if this declaration is not yet ready for printing, move 214 | // on to the next one in the list 215 | } 216 | } 217 | if (!printedSomething) 218 | possibleLoop = true; 219 | } 220 | output += "]]\n"; 221 | 222 | if (utils->hasMarkedDeclarations()) { 223 | llvm::raw_fd_ostream *fileOutput = new llvm::raw_fd_ostream( 224 | utils->getDestinationDirectory() + outputFileName, Err, 225 | llvm::sys::fs::F_RW); 226 | if (Err) { 227 | llvm::errs() << "Error creating file \"" << outputFileName 228 | << "\" : " << Err.message() << "!\n"; 229 | return; 230 | } 231 | (*fileOutput) << output; 232 | (*fileOutput).close(); 233 | delete fileOutput; 234 | } 235 | delete utils; 236 | } 237 | 238 | private: 239 | FunctionVisitor FunctionsVisitor; 240 | RecordVisitor RecordsVisitor; 241 | EnumVisitor EnumsVisitor; 242 | TypedefVisitor TypedefsVisitor; 243 | std::string output; 244 | FFIBindingsUtils *utils; 245 | 246 | /** 247 | * Determine whether this declaration depends on itself (directly or 248 | * indirectly). 249 | */ 250 | bool DependsOnItself(std::string DeclName, 251 | std::vector *dependencyList) { 252 | // Direct and indirect dependencies of this declaration 253 | std::stack Dependencies; 254 | // Declarations that have already been checked 255 | std::set CheckedDecls; 256 | 257 | for (std::vector::iterator dependency = 258 | dependencyList->begin(); 259 | dependency != dependencyList->end(); ++dependency) { 260 | if (utils->isInUnresolvedDeclarations(*dependency)) { 261 | Dependencies.push(*dependency); 262 | CheckedDecls.insert(*dependency); 263 | } 264 | } 265 | while (Dependencies.size()) { 266 | std::string DeclToCheck = Dependencies.top(); 267 | Dependencies.pop(); 268 | if (DeclToCheck == DeclName) 269 | return true; 270 | DeclarationInfo depDecl = 271 | utils->getUnresolvedDeclarations()->at(DeclToCheck); 272 | if (!utils->isInResolvedDecls(DeclToCheck) && 273 | !DependsOnItselfDirectly(DeclToCheck, depDecl.dependencyList)) { 274 | for (std::vector::iterator dependency = 275 | depDecl.dependencyList->begin(); 276 | dependency != depDecl.dependencyList->end(); ++dependency) { 277 | if (utils->isInUnresolvedDeclarations(*dependency) && 278 | (CheckedDecls.find(*dependency) == CheckedDecls.end())) { 279 | Dependencies.push(*dependency); 280 | CheckedDecls.insert(*dependency); 281 | } 282 | } 283 | } 284 | } 285 | return false; 286 | } 287 | 288 | /** 289 | * Determine whether this declaration depends on itself directly. 290 | */ 291 | bool DependsOnItselfDirectly(std::string DeclName, 292 | std::vector *dependencyList) { 293 | for (std::vector::iterator dependency = 294 | dependencyList->begin(); 295 | dependency != dependencyList->end(); ++dependency) { 296 | if (*dependency == DeclName) 297 | return true; 298 | } 299 | return false; 300 | } 301 | }; 302 | 303 | class GenerateFFIBindingsAction : public PluginASTAction { 304 | protected: 305 | std::unique_ptr CreateASTConsumer(CompilerInstance &CI, 306 | llvm::StringRef inputFile) { 307 | 308 | FFIBindingsUtils *utils = FFIBindingsUtils::getInstance(); 309 | 310 | if (utils->getOutputFileName() == "") { 311 | std::string filename = inputFile; 312 | char separator; 313 | #ifdef LLVM_ON_UNIX 314 | separator = '/'; 315 | #else 316 | separator = '\\'; 317 | #endif 318 | if (filename.find_last_of(separator) != std::string::npos) 319 | filename = filename.substr(filename.find_last_of(separator) + 1); 320 | 321 | filename.replace(filename.find_first_of('.'), 322 | filename.length() - filename.find_first_of('.'), ""); 323 | filename += "_gen_ffi.lua"; 324 | utils->setOutputFileName(filename); 325 | } 326 | return llvm::make_unique(); 327 | } 328 | 329 | bool ParseArgs(const CompilerInstance &CI, 330 | const std::vector &args) { 331 | 332 | FFIBindingsUtils *utils = FFIBindingsUtils::getInstance(); 333 | 334 | for (unsigned i = 0, e = args.size(); i != e; ++i) { 335 | 336 | if (args[i] == "help") { 337 | PrintHelp(llvm::errs()); 338 | return true; 339 | } 340 | 341 | if (args[i] == "test") 342 | utils->setTestingMode(true); 343 | 344 | if (args[i] == "-output") { 345 | if (args.size() >= i + 2) 346 | utils->setOutputFileName(args[i + 1]); 347 | else 348 | llvm::outs() << "Enter output file name.\n"; 349 | } 350 | 351 | if (args[i] == "-header") { 352 | if (args.size() >= i + 2) 353 | utils->setHeaderFileName(args[i + 1]); 354 | else 355 | llvm::outs() << "Enter header file name.\n"; 356 | } 357 | 358 | if (args[i] == "-blacklist") { 359 | if (args.size() >= i + 2) 360 | utils->setBlacklistFileName(args[i + 1]); 361 | else 362 | llvm::outs() 363 | << "Enter name of the file containing type blacklist. \n"; 364 | } 365 | 366 | if (args[i] == "-destdir") { 367 | if (args.size() >= i + 2) { 368 | char separator; 369 | #ifdef LLVM_ON_UNIX 370 | separator = '/'; 371 | #else 372 | separator = '\\'; 373 | #endif 374 | std::string destDir = args[i + 1]; 375 | if (destDir.size() > 0) { 376 | int end = destDir.size() - 1; 377 | if (destDir[end] != separator) 378 | destDir += separator; 379 | } 380 | utils->setDestinationDirectory(destDir); 381 | } else 382 | llvm::outs() << "Enter path of the destination directory.\n"; 383 | } 384 | } 385 | return true; 386 | } 387 | 388 | void PrintHelp(llvm::raw_ostream &ros) { 389 | ros << "----------------------------------\n"; 390 | ros << "---------- ffi-gen help ----------\n"; 391 | ros << "----------------------------------\n\n"; 392 | ros << "Options:\n"; 393 | ros << " -output Specifies output file (generated Lua file). Default " 394 | "file name is \"output.lua\".\n"; 395 | ros << " -header Specifies text file that contains a header to put in " 396 | "the generated file.\n"; 397 | ros << " -blacklist Specifies text file that contains list of types " 398 | "that should not be emitted or resolved.\n"; 399 | ros << " -destdir Specifies path to the destination directory. This is " 400 | "the directory where output Lua file will be generated.\n"; 401 | ros << " test Turns on test mode. When in test mode,\n" 402 | " the plugin generates bindings for each function,\n" 403 | " whether it was marked with the ffibinding attribute " 404 | "or not.\n" 405 | " When in this mode, name of the generated output file " 406 | "is\n" 407 | " formed from the absolute path of the source file.\n"; 408 | ros << " To enable test mode add: -Xclang -plugin-arg-ffi-gen " 409 | "-Xclang test\n"; 410 | ros << "These options and their values must be preceded by " 411 | "\"-plugin-arg-ffi-gen\" " 412 | "option.\n\n"; 413 | 414 | ros << "Example of running the plugin:\n"; 415 | ros << "/clang test.c -c -Xclang -load -Xclang " 416 | "/ffi-gen.so -Xclang -plugin -Xclang ffi-gen " 417 | "-Xclang -plugin-arg-ffi-gen -Xclang -output -Xclang " 418 | "-plugin-arg-ffi-gen -Xclang test.lua " 419 | "-Xclang -plugin-arg-ffi-gen -Xclang -header -Xclang " 420 | "-plugin-arg-ffi-gen -Xclang test.txt " 421 | "-Xclang -plugin-arg-ffi-gen -Xclang -blacklist -Xclang " 422 | "-plugin-arg-ffi-gen -Xclang blacklist.txt\n\n"; 423 | ros << "This will generate LuaJIT ffi bindings for functions, structs and " 424 | "unions \nmarked with the ffibinding attribute from the input file " 425 | "\"test.c\" \nand write the generated bindings to a file named " 426 | "\"test.lua\".\nIt will also copy the contents of the \"test.txt\" " 427 | "file at the top of the \"test.lua\" file.\nOutput file will not " 428 | "contain bindings for types that are listed in the " 429 | "\"blacklist.txt\" " 430 | "file.\n\n"; 431 | } 432 | }; 433 | 434 | static FrontendPluginRegistry::Add 435 | X("ffi-gen", "generate LuaJIT ffi bindings"); 436 | -------------------------------------------------------------------------------- /GenerateFFIBindings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GENERATEFFIBINDINGS_H 2 | #define GENERATEFFIBINDINGS_H 3 | 4 | #include "clang/Frontend/FrontendPluginRegistry.h" 5 | #include "clang/AST/AST.h" 6 | #include "clang/AST/ASTConsumer.h" 7 | #include "clang/Frontend/CompilerInstance.h" 8 | #include "llvm/Support/raw_ostream.h" 9 | #include "clang/AST/RecursiveASTVisitor.h" 10 | #include 11 | using namespace clang; 12 | 13 | namespace constants { 14 | const std::string SRC_FILE_PLACE_HOLDER = ""; 15 | } 16 | 17 | /** 18 | * Data containing the declaration node (with information attached to it) and 19 | *additional information. 20 | **/ 21 | struct TypeDeclaration { 22 | std::string TypeName; 23 | /** Declaration node. The information that it contains depends on the type of 24 | * the declaration (function, struct, enum, etc.). */ 25 | TypeDecl *Declaration; 26 | }; 27 | 28 | /** 29 | * Information about a declaration needed for it to be resolved and 30 | * printed in the right order (after declarations that it depends on). 31 | * 32 | **/ 33 | struct DeclarationInfo { 34 | /** Full declaration (this is what will be printed). */ 35 | std::string Declaration; 36 | /** Is the declaration already resolved. */ 37 | bool isResolved = true; 38 | /** A list of declarations this declaration depends on (these declarations 39 | * need to be printed out before it). */ 40 | std::vector *dependencyList; 41 | }; 42 | 43 | /** 44 | * Finds specified functions and gathers data about them that's needed 45 | * to resolve them (print them out). 46 | **/ 47 | class FunctionVisitor : public RecursiveASTVisitor { 48 | public: 49 | FunctionVisitor() {} 50 | /** Visits function declarations in a parsed AST. */ 51 | bool VisitFunctionDecl(FunctionDecl *FD); 52 | }; 53 | 54 | /** 55 | * Finds records (struct, union) declarations and gathers data about them 56 | * that's needed to resolve them (print them out). 57 | **/ 58 | class RecordVisitor : public RecursiveASTVisitor { 59 | public: 60 | RecordVisitor() {} 61 | /** Visits record declarations in a parsed AST. */ 62 | bool VisitRecordDecl(RecordDecl *RD); 63 | }; 64 | 65 | /** 66 | * Finds enumerations marked with the ffibinding attribute and prints them out. 67 | **/ 68 | class EnumVisitor : public RecursiveASTVisitor { 69 | public: 70 | EnumVisitor() {} 71 | /** Visits enum declarations in a parsed AST. */ 72 | bool VisitEnumDecl(EnumDecl *ED); 73 | }; 74 | 75 | /** 76 | * Finds typedefs marked with the ffibinding attribute and gathers data about 77 | * them that's needed to resolve them (print them out) 78 | **/ 79 | class TypedefVisitor : public RecursiveASTVisitor { 80 | public: 81 | TypedefVisitor() {} 82 | /** Visits typedef declarations in a parsed AST. */ 83 | bool VisitTypedefDecl(TypedefNameDecl *TD); 84 | }; 85 | 86 | class FFIBindingsUtils { 87 | public: 88 | /** Type passed to checkType(), to determine whether the type being 89 | * checked is function parameter or return value, or neither. 90 | */ 91 | enum ParamType { 92 | RETVAL, 93 | PARAM, 94 | NONE 95 | }; 96 | /** Type passed to checkType(), to determine whether the type being 97 | * checked came from a function or other declaration. 98 | */ 99 | enum ParentDeclType { 100 | FUNCTION, 101 | NORMAL 102 | }; 103 | 104 | static FFIBindingsUtils *getInstance(); 105 | /** Is this the first time to come across given declaration type. 106 | * Returns false if the type is already resolved, waiting to be resolved or 107 | * printed out, true otherwise. */ 108 | bool isNewType(QualType Type); 109 | /** Returns true if given declaration type is in the UnresolvedDeclarations 110 | * list, false otherwise. */ 111 | bool isInUnresolvedDeclarations(std::string DeclType); 112 | /** Returns true if given declaration type is in the ResolvedDecls 113 | * list, false otherwise. */ 114 | bool isInResolvedDecls(std::string DeclType); 115 | /** Returns true if this type should not be resolved and emitted because it is 116 | * on the blacklist, false otherwise. */ 117 | bool isOnBlacklist(std::string DeclType); 118 | /** Get size of the array (e.g. for "double arr[6]" return value would be 119 | * "[6]"). */ 120 | std::string getArraySize(const ConstantArrayType *CAT); 121 | /** Returns a string containing the list of attributes attached to this 122 | * Decl.*/ 123 | std::string getDeclAttrs(Decl *RD); 124 | 125 | std::map *getUnresolvedDeclarations() { 126 | return UnresolvedDeclarations; 127 | } 128 | 129 | std::stack *getDeclsToFind() { return DeclsToFind; } 130 | 131 | std::set *getResolvedDecls() { return ResolvedDecls; } 132 | 133 | std::string getOutputFileName() { return outputFileName; } 134 | 135 | void setOutputFileName(std::string filename) { outputFileName = filename; } 136 | 137 | std::string getHeaderFileName() { return headerFileName; } 138 | 139 | void setHeaderFileName(std::string filename) { headerFileName = filename; } 140 | 141 | std::string getBlacklistFileName() { return blacklistFileName; } 142 | 143 | void setBlacklistFileName(std::string filename) { 144 | blacklistFileName = filename; 145 | } 146 | 147 | std::string getDestinationDirectory() { return destinationDirectory; } 148 | 149 | void setDestinationDirectory(std::string destDir) { 150 | destinationDirectory = destDir; 151 | } 152 | 153 | std::set *getBlacklist() { return blacklist; } 154 | 155 | bool isTestingModeOn() { return isTestingMode; } 156 | 157 | void setTestingMode(bool testingMode) { isTestingMode = testingMode; } 158 | 159 | std::vector *getAnonymousRecords() { return AnonymousRecords; } 160 | 161 | ~FFIBindingsUtils() { 162 | delete UnresolvedDeclarations; 163 | delete DeclsToFind; 164 | delete ResolvedDecls; 165 | delete blacklist; 166 | delete AnonymousRecords; 167 | } 168 | 169 | bool hasMarkedDeclarations() { return markedDeclarations; } 170 | 171 | void setHasMarkedDeclarations(bool markedDeclarations_) { 172 | markedDeclarations = markedDeclarations_; 173 | } 174 | 175 | void setOutput(std::string *output_) { output = output_; } 176 | 177 | void setContext(ASTContext *astContext) { Context = astContext; } 178 | 179 | /** Try to resolve given anonymous record declaration. */ 180 | void resolveAnonRecord(RecordDecl *RD); 181 | 182 | /** Prints out the given enum declaration. */ 183 | void resolveEnumDecl(EnumDecl *ED); 184 | 185 | /** Tries to resolve given function declaration. If it can be immediately 186 | * resolved it is printed out, otherwise further actions that are needed for 187 | * it to be resolved are done. */ 188 | void resolveFunctionDecl(FunctionDecl *FD); 189 | 190 | /** Tries to resolve given record declaration. If it can be immediately 191 | * resolved it is printed out, otherwise further actions that are needed for 192 | * it to be resolved are done. */ 193 | void resolveRecordDecl(RecordDecl *RD); 194 | 195 | /** Tries to resolve given typedef declaration. If it can be immediately 196 | * resolved it is printed out, otherwise further actions that are needed for 197 | * it to be resolved are done. */ 198 | void resolveTypedefDecl(TypedefNameDecl *TD); 199 | 200 | /** Add this type to the parent declaration for printing and determine whether 201 | * the parent depends on it and needs to wait for it to be resolved. 202 | * @param Type Type to check 203 | * @param isResolved Flag to determine whether parent declaration is 204 | * resolved or not 205 | * @param dependencyList List of types that parent declaration depends on 206 | * @param DeclarationCore Current parent declaration (what will be printed). 207 | * This type should be appended to it in a certain way. 208 | * @param parentType Type of the parent declaration (function or other) 209 | * @param parameterType Type of the type being checked (parameter (PARAM), 210 | * return value (RETVAL) or neither (NONE). Used in the case when parentType 211 | * is FUNCTION. 212 | * */ 213 | void checkType(QualType Type, bool *isResolved, 214 | std::vector *dependencyList, 215 | std::string &DeclarationCore, enum ParentDeclType parentType, 216 | enum ParamType parameterType); 217 | 218 | private: 219 | static FFIBindingsUtils *instance; 220 | FFIBindingsUtils() { 221 | UnresolvedDeclarations = new std::map(); 222 | DeclsToFind = new std::stack(); 223 | ResolvedDecls = new std::set(); 224 | blacklist = new std::set(); 225 | AnonymousRecords = new std::vector(); 226 | } 227 | FFIBindingsUtils(FFIBindingsUtils &); 228 | FFIBindingsUtils &operator=(FFIBindingsUtils &); 229 | 230 | /** A map containing pairs of declaration names (types) and additional 231 | * information about them (e.g. a list of declarations they depend on). 232 | * This map contains declarations that cannot be resolved immediately 233 | * (contain non-primitive types). */ 234 | std::map *UnresolvedDeclarations; 235 | /** A list of declarations that need to be found. */ 236 | std::stack *DeclsToFind; 237 | /** A list of resolved (printed out) declarations. */ 238 | std::set *ResolvedDecls; 239 | std::vector *AnonymousRecords; 240 | std::string outputFileName = ""; 241 | std::string headerFileName = ""; 242 | std::string blacklistFileName = ""; 243 | std::string destinationDirectory = ""; 244 | std::set *blacklist; 245 | ASTContext *Context; 246 | /** Output string for writing the output to a file. */ 247 | std::string *output; 248 | /** This flag is set to true when 'test' is passed on the command line. */ 249 | bool isTestingMode = false; 250 | /** Used to check if the plugin should generate an output .lua file. Remains 251 | * false if there are no declarations marked with the ffibinding attribute. */ 252 | bool markedDeclarations = false; 253 | }; 254 | #endif /* GENERATEFFIBINDINGS_H */ 255 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LLVM Release License 3 | ============================================================================== 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2003-2014 University of Illinois at Urbana-Champaign. 8 | All rights reserved. 9 | 10 | Developed by: 11 | 12 | LLVM Team 13 | 14 | University of Illinois at Urbana-Champaign 15 | 16 | http://llvm.org 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal with 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | * Redistributions of source code must retain the above copyright notice, 26 | this list of conditions and the following disclaimers. 27 | 28 | * Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimers in the 30 | documentation and/or other materials provided with the distribution. 31 | 32 | * Neither the names of the LLVM Team, University of Illinois at 33 | Urbana-Champaign, nor the names of its contributors may be used to 34 | endorse or promote products derived from this Software without specific 35 | prior written permission. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 39 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 43 | SOFTWARE. 44 | 45 | ============================================================================== 46 | Copyrights and Licenses for Third Party Software Distributed with LLVM: 47 | ============================================================================== 48 | The LLVM software contains code written by third parties. Such software will 49 | have its own individual LICENSE.TXT file in the directory in which it appears. 50 | This file will describe the copyrights, license, and restrictions which apply 51 | to that code. 52 | 53 | The disclaimer of warranty in the University of Illinois Open Source License 54 | applies to all code in the LLVM Distribution, and nothing in any of the 55 | other licenses gives permission to use the names of the LLVM Team or the 56 | University of Illinois to endorse or promote products derived from this 57 | Software. 58 | 59 | The following pieces of software have additional or alternate copyrights, 60 | licenses, and/or restrictions: 61 | 62 | Program Directory 63 | ------- --------- 64 | Autoconf llvm/autoconf 65 | llvm/projects/ModuleMaker/autoconf 66 | Google Test llvm/utils/unittest/googletest 67 | OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex} 68 | pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT} 69 | ARM contributions llvm/lib/Target/ARM/LICENSE.TXT 70 | md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h 71 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ##===- examples/ffi-gen/Makefile ----------------*- Makefile -*-===## 2 | # 3 | # The LLVM Compiler Infrastructure 4 | # 5 | # This file is distributed under the University of Illinois Open Source 6 | # License. See LICENSE.TXT for details. 7 | # 8 | ##===----------------------------------------------------------------------===## 9 | 10 | CLANG_LEVEL := ../.. 11 | LIBRARYNAME = ffi-gen 12 | 13 | # If we don't need RTTI or EH, there's no reason to export anything 14 | # from the plugin. 15 | ifneq ($(REQUIRES_RTTI), 1) 16 | ifneq ($(REQUIRES_EH), 1) 17 | EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/ffi-gen.exports 18 | endif 19 | endif 20 | 21 | LINK_LIBS_IN_SHARED = 0 22 | LOADABLE_MODULE = 1 23 | 24 | include $(CLANG_LEVEL)/Makefile 25 | 26 | ifeq ($(OS),Darwin) 27 | LDFLAGS=-Wl,-undefined,dynamic_lookup 28 | endif 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-ffi-gen 2 | 3 | A Clang plugin for generating declarations for the LuaJIT FFI library. 4 | 5 | Basic usage information 6 | 7 | Mark the declarations of functions, structures/unions or enums in C code that you want to use inside Lua code. Build the C code with Clang and run the implemented plugin. The plugin will generate bindings for marked declarations in an output Lua file. It will find all declarations of types that are needed for using marked declarations in Lua code. 8 | 9 | Build instructions and usage details can be found in the wiki section. 10 | -------------------------------------------------------------------------------- /RecordVisitor.cpp: -------------------------------------------------------------------------------- 1 | #include "GenerateFFIBindings.hpp" 2 | 3 | bool RecordVisitor::VisitRecordDecl(RecordDecl *RD) { 4 | // try to resolve record declaration if it has ffibinding attribute 5 | if (!RD->hasAttr()) 6 | return true; 7 | 8 | // if this record type has already been resolved, then there's nothing to do 9 | if (!FFIBindingsUtils::getInstance()->isNewType( 10 | RD->getTypeForDecl()->getCanonicalTypeInternal())) 11 | return true; 12 | 13 | FFIBindingsUtils::getInstance()->setHasMarkedDeclarations(true); 14 | FFIBindingsUtils::getInstance()->resolveRecordDecl(RD); 15 | 16 | return true; 17 | } 18 | -------------------------------------------------------------------------------- /TypedefVisitor.cpp: -------------------------------------------------------------------------------- 1 | #include "GenerateFFIBindings.hpp" 2 | 3 | bool TypedefVisitor::VisitTypedefDecl(TypedefNameDecl *TD) { 4 | if (!TD->hasAttr()) 5 | return true; 6 | 7 | if (FFIBindingsUtils::getInstance()->isInResolvedDecls("typedef " + 8 | TD->getNameAsString())) 9 | return true; 10 | 11 | if (FFIBindingsUtils::getInstance()->isOnBlacklist(TD->getNameAsString())) { 12 | FFIBindingsUtils::getInstance()->getResolvedDecls()->insert( 13 | "typedef " + TD->getNameAsString()); 14 | return true; 15 | } 16 | 17 | FFIBindingsUtils::getInstance()->setHasMarkedDeclarations(true); 18 | FFIBindingsUtils::getInstance()->resolveTypedefDecl(TD); 19 | 20 | return true; 21 | } 22 | -------------------------------------------------------------------------------- /clang-patch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt 2 | index 5d4b5fc..d7e6586 100644 3 | --- a/examples/CMakeLists.txt 4 | +++ b/examples/CMakeLists.txt 5 | @@ -8,3 +8,4 @@ add_subdirectory(analyzer-plugin) 6 | endif() 7 | add_subdirectory(clang-interpreter) 8 | add_subdirectory(PrintFunctionNames) 9 | +add_subdirectory(ffi-gen) 10 | \ No newline at end of file 11 | diff --git a/examples/Makefile b/examples/Makefile 12 | index d8d9028..35d4fb3 100644 13 | --- a/examples/Makefile 14 | +++ b/examples/Makefile 15 | @@ -9,6 +9,6 @@ 16 | 17 | CLANG_LEVEL := .. 18 | 19 | -PARALLEL_DIRS := analyzer-plugin clang-interpreter PrintFunctionNames 20 | +PARALLEL_DIRS := analyzer-plugin clang-interpreter PrintFunctionNames ffi-gen 21 | 22 | include $(CLANG_LEVEL)/Makefile 23 | diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td 24 | index 3ed7f8d..8a5b4d3 100644 25 | --- a/include/clang/Basic/Attr.td 26 | +++ b/include/clang/Basic/Attr.td 27 | @@ -1969,6 +1969,12 @@ def CapturedRecord : InheritableAttr { 28 | let Documentation = [Undocumented]; 29 | } 30 | 31 | +def FFIBinding : Attr { 32 | + let Spellings = [GCC<"ffibinding">, Keyword<"__ffibinding">, 33 | + Keyword<"_ffibinding">]; 34 | + let Documentation = [Undocumented]; 35 | +} 36 | + 37 | def OMPThreadPrivateDecl : InheritableAttr { 38 | // This attribute has no spellings as it is only ever created implicitly. 39 | let Spellings = []; 40 | diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td 41 | index b68a46b..f0e309d 100644 42 | --- a/include/clang/Driver/Options.td 43 | +++ b/include/clang/Driver/Options.td 44 | @@ -1860,6 +1860,19 @@ defm stack_arrays : BooleanFFlag<"stack-arrays">, Group; 45 | defm underscoring : BooleanFFlag<"underscoring">, Group; 46 | defm whole_file : BooleanFFlag<"whole-file">, Group; 47 | 48 | +// ffi-gen plugin options 49 | +def ffi_gen_enable : Flag<["-"], "ffi-gen-enable">, 50 | + HelpText<"Load ffi-gen plugin.">; 51 | +def ffi_gen_test : Flag<["-"], "ffi-gen-test">, 52 | + HelpText<"Pass argument 'test' to ffi-gen plugin.">; 53 | +def ffi_gen_output : Separate<["-"], "ffi-gen-output">, 54 | + HelpText<"Pass argument -output to ffi-gen plugin.">; 55 | +def ffi_gen_header : Separate<["-"], "ffi-gen-header">, 56 | + HelpText<"Pass argument -header to ffi-gen plugin.">; 57 | +def ffi_gen_blacklist : Separate<["-"], "ffi-gen-blacklist">, 58 | + HelpText<"Pass argument -blacklist to ffi-gen plugin.">; 59 | +def ffi_gen_destdir : Separate<["-"], "ffi-gen-destdir">, 60 | + HelpText<"Pass argument -destdir to ffi-gen plugin.">; 61 | 62 | include "CC1Options.td" 63 | 64 | diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp 65 | index 75eef9e..d8eb46a 100644 66 | --- a/lib/Driver/Tools.cpp 67 | +++ b/lib/Driver/Tools.cpp 68 | @@ -4430,6 +4430,51 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, 69 | // Forward -fparse-all-comments to -cc1. 70 | Args.AddAllArgs(CmdArgs, options::OPT_fparse_all_comments); 71 | 72 | + // handle ffi-gen plugin options 73 | + if (Args.hasArg(options::OPT_ffi_gen_enable)) { 74 | + CmdArgs.push_back("-load"); 75 | + CmdArgs.push_back(Args.MakeArgString(D.Dir + "/../lib/ffi-gen.so")); 76 | + CmdArgs.push_back("-add-plugin"); 77 | + CmdArgs.push_back("ffi-gen"); 78 | + 79 | + if (Args.hasArg(options::OPT_ffi_gen_test)) { 80 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 81 | + CmdArgs.push_back("test"); 82 | + } 83 | + 84 | + if (Args.hasArg(options::OPT_ffi_gen_output)) { 85 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 86 | + CmdArgs.push_back("-output"); 87 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 88 | + Arg *A = Args.getLastArg(options::OPT_ffi_gen_output); 89 | + CmdArgs.push_back(A->getValue(0)); 90 | + } 91 | + 92 | + if (Args.hasArg(options::OPT_ffi_gen_header)) { 93 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 94 | + CmdArgs.push_back("-header"); 95 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 96 | + Arg *A = Args.getLastArg(options::OPT_ffi_gen_header); 97 | + CmdArgs.push_back(A->getValue(0)); 98 | + } 99 | + 100 | + if (Args.hasArg(options::OPT_ffi_gen_blacklist)) { 101 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 102 | + CmdArgs.push_back("-blacklist"); 103 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 104 | + Arg *A = Args.getLastArg(options::OPT_ffi_gen_blacklist); 105 | + CmdArgs.push_back(A->getValue(0)); 106 | + } 107 | + 108 | + if (Args.hasArg(options::OPT_ffi_gen_destdir)) { 109 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 110 | + CmdArgs.push_back("-destdir"); 111 | + CmdArgs.push_back("-plugin-arg-ffi-gen"); 112 | + Arg *A = Args.getLastArg(options::OPT_ffi_gen_destdir); 113 | + CmdArgs.push_back(A->getValue(0)); 114 | + } 115 | + } 116 | + 117 | // Forward -Xclang arguments to -cc1, and -mllvm arguments to the LLVM option 118 | // parser. 119 | Args.AddAllArgValues(CmdArgs, options::OPT_Xclang); 120 | diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp 121 | index dcab25e..31fbcde 100644 122 | --- a/lib/Sema/SemaDeclAttr.cpp 123 | +++ b/lib/Sema/SemaDeclAttr.cpp 124 | @@ -4780,6 +4780,11 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, 125 | case AttributeList::AT_TypeTagForDatatype: 126 | handleTypeTagForDatatypeAttr(S, D, Attr); 127 | break; 128 | + 129 | + // LuaJIT FFI binding attribute 130 | + case AttributeList::AT_FFIBinding: 131 | + handleSimpleAttribute(S, D, Attr); 132 | + break; 133 | } 134 | } 135 | 136 | -------------------------------------------------------------------------------- /ffi-combine.lua: -------------------------------------------------------------------------------- 1 | local headerBegin = "--[[" 2 | local headerEnd = "--]]" 3 | 4 | local ffiBlockBegin = "ffi.cdef[[" 5 | local ffiBlockEnd = "]]" 6 | 7 | local sourceListPlaceHolder = "" 8 | -- a hyphen is a special character so it must be preceded by "%" escape character 9 | 10 | local outputOption = "--output=" 11 | local outputFileName = "out.lua" -- default output file name 12 | 13 | local headerOption = "--header=" 14 | local headerFileName = "" 15 | local footerOption = "--footer=" 16 | local footerFileName = "" 17 | 18 | local inputFiles = {} -- list of files to combine 19 | local bindings = {} -- map of bindings 20 | local output = {} -- list of lines to write to the output file 21 | local sourceFiles = {} -- list of source files that given lua files are based on 22 | 23 | local sourcesLineIndex = -1 24 | 25 | -- this function determines whether the specified table contains the given element 26 | function table.contains(table, element) 27 | for _, value in pairs(table) do 28 | if value == element then 29 | return true 30 | end 31 | end 32 | return false 33 | end 34 | 35 | -- get names of the lua files to combine (and if set, output, header and footer file names) 36 | for i = 1, #arg do 37 | if string.sub(arg[i], 1, string.len(outputOption)) == outputOption then 38 | outputFileName = string.sub(arg[i], string.len(outputOption) + 1) 39 | elseif string.sub(arg[i], 1, string.len(headerOption)) == headerOption then 40 | headerFileName = string.sub(arg[i], string.len(headerOption) + 1) 41 | elseif string.sub(arg[i], 1, string.len(footerOption)) == footerOption then 42 | footerFileName = string.sub(arg[i], string.len(footerOption) + 1) 43 | else 44 | table.insert(inputFiles, arg[i]) 45 | end 46 | end 47 | 48 | if headerFileName ~= "" then 49 | local headerFile = io.open(headerFileName, "r") 50 | if not headerFile then 51 | error("File: " .. headerFileName .. " does not exist.") 52 | end 53 | io.close(headerFile) 54 | 55 | for line in io.lines(headerFileName) do 56 | if string.find(line, sourceListPlaceHolder) then 57 | -- line containing "" token is not copied to the output 58 | -- it serves as an indicator of where list of source files should be inserted 59 | sourcesLineIndex = #output 60 | else 61 | -- other lines in the header are copied to the output 62 | table.insert(output, line .. "\n") 63 | end 64 | end 65 | end 66 | 67 | table.insert(output, "ffi = require(\"ffi\")\nffi.cdef[[") 68 | 69 | for _, fileName in ipairs(inputFiles) do 70 | 71 | local inputFile = io.open(fileName , "r") 72 | if not inputFile then 73 | error("File: " .. fileName .. " does not exist.") 74 | end 75 | io.close(inputFile) 76 | 77 | local isStartBlock = true 78 | local isEndBlock = false 79 | 80 | local isInHeader = false 81 | 82 | local currentBinding = "" 83 | 84 | for line in io.lines(fileName) do 85 | 86 | if line == headerBegin then 87 | isInHeader = true 88 | end 89 | 90 | if isInHeader then 91 | if string.find(line, ">> ") then 92 | if not table.contains(sourceFiles, line) then 93 | table.insert(sourceFiles, line) 94 | end 95 | end 96 | end 97 | 98 | if line == ffiBlockEnd then 99 | isEndBlock = true 100 | end 101 | 102 | if not isStartBlock and not isEndBlock then 103 | if not line or line == "" then 104 | if (currentBinding ~= "") then 105 | if not bindings[currentBinding] then 106 | table.insert(output, "\n") 107 | table.insert(output, currentBinding) 108 | bindings[currentBinding] = currentBinding 109 | end 110 | end 111 | currentBinding = "" 112 | else 113 | currentBinding = currentBinding .. "\n" .. line 114 | end 115 | end 116 | 117 | if line == ffiBlockBegin then 118 | isStartBlock = false 119 | end 120 | 121 | if line == headerEnd then 122 | isInHeader = false 123 | end 124 | 125 | end 126 | end 127 | 128 | if sourcesLineIndex ~= -1 then 129 | -- insert list of source files (each in new line) 130 | for i, sourceFileName in ipairs(sourceFiles) do 131 | table.insert(output, sourcesLineIndex + i, sourceFileName .. "\n") 132 | end 133 | end 134 | 135 | table.insert(output, "\n\n]]\n") 136 | 137 | if footerFileName ~= "" then 138 | local footerFile = io.open(footerFileName, "r") 139 | if not footerFile then 140 | error("File: " .. footerFileName .. " does not exist.") 141 | end 142 | io.close(footerFile) 143 | 144 | for line in io.lines(footerFileName) do 145 | table.insert(output, line .. "\n") 146 | end 147 | end 148 | 149 | -- write output to a file 150 | local outFile = io.open(outputFileName , "w") 151 | if not outFile then 152 | error("Cannot write to file: " .. outputFileName) 153 | end 154 | 155 | for _, outputLine in ipairs(output) do 156 | outFile:write(outputLine) 157 | end 158 | io.close(outFile) 159 | -------------------------------------------------------------------------------- /ffi-combine.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | IFS='' # prevent stripping leading spaces on file read 3 | 4 | headerBegin="--[[" 5 | headerEnd="--]]" 6 | 7 | ffiBlockBegin="ffi.cdef[[" 8 | ffiBlockEnd="]]" 9 | 10 | sourceListPlaceHolder="" 11 | 12 | outputOption="--output=" 13 | outputFileName="out.lua" # default output file name 14 | 15 | headerOption="--header=" 16 | headerFileName="" 17 | footerOption="--footer=" 18 | footerFileName="" 19 | 20 | inputFiles=() # list of files to combine 21 | declare -A bindings # map of bindings 22 | output=() # list of lines to write to the output file 23 | sourceFiles=() # list of source files that given lua files are based on 24 | 25 | sourcesLineIndex=-1 26 | 27 | # this function determines whether the specified table contains the given element 28 | function tableContains() { 29 | declare -a table=("${!1}") 30 | element="$2" 31 | for value in ${table[@]}; do 32 | if [ "$value" == "$element" ]; 33 | then 34 | return 1 35 | fi 36 | done 37 | return 0 38 | } 39 | 40 | # get names of the lua files to combine (and if set, output, header and footer file names) 41 | for arg in "$@"; do 42 | if [ "${arg:0:${#outputOption}}" == "$outputOption" ]; 43 | then 44 | outputFileName=${arg:${#outputOption}} 45 | elif [ "${arg:0:${#headerOption}}" == "$headerOption" ]; 46 | then 47 | headerFileName=${arg:${#headerOption}} 48 | elif [ "${arg:0:${#footerOption}}" == "$footerOption" ]; 49 | then 50 | footerFileName=${arg:${#footerOption}} 51 | else 52 | inputFiles[${#inputFiles[@]}]=$arg 53 | fi 54 | done 55 | 56 | if [ "$headerFileName" != "" ]; 57 | then 58 | if [ ! -e $headerFileName ]; then 59 | echo "File: $headerFileName does not exist." 60 | exit 61 | fi 62 | while read -r line ; do 63 | if [[ "$line" == *"$sourceListPlaceHolder"* ]]; 64 | then 65 | # line containing "" token is not copied to the output 66 | # it serves as an indicator of where list of source files should be inserted 67 | sourcesLineIndex=${#output[@]} 68 | else 69 | # other lines in the header are copied to the output 70 | output[${#output[@]}]="$line\n" 71 | fi 72 | done < "$headerFileName" 73 | 74 | fi 75 | 76 | output[${#output[@]}]="ffi = require(\"ffi\")\nffi.cdef[[" 77 | 78 | for fileName in ${inputFiles[@]}; do 79 | if [ ! -e $fileName ]; 80 | then 81 | echo "File: $fileName does not exist." 82 | exit 83 | fi 84 | isStartBlock=1 85 | isEndBlock=0 86 | isInHeader=0 87 | currentBinding="" 88 | while read line ; do 89 | if [ "$line" == "$headerBegin" ]; 90 | then 91 | isInHeader=1 92 | fi 93 | if [ $isInHeader -eq 1 ]; 94 | then 95 | if [[ "$line" == *">> "* ]]; 96 | then 97 | tableContains sourceFiles[@] "$line" 98 | res=$? 99 | if [ $res -ne 1 ]; 100 | then 101 | sourceFiles[${#sourceFiles[@]}]="$line"; 102 | fi 103 | fi 104 | fi 105 | if [ "$line" == "$ffiBlockEnd" ]; 106 | then 107 | isEndBlock=1 108 | fi 109 | if [ $isStartBlock -ne 1 ] && [ $isEndBlock -ne 1 ]; 110 | then 111 | if [ -z "$line" ] || [ "$line" == "" ]; 112 | then 113 | if [ "$currentBinding" != "" ]; 114 | then 115 | if [ "${bindings[$currentBinding]}" == "" ]; 116 | then 117 | output[${#output[@]}]="\n" 118 | output[${#output[@]}]=$currentBinding 119 | bindings["$currentBinding"]=$currentBinding 120 | fi 121 | fi 122 | currentBinding="" 123 | else 124 | currentBinding="$currentBinding\n$line" 125 | fi 126 | fi 127 | if [ "$line" == "$ffiBlockBegin" ]; 128 | then 129 | isStartBlock=0 130 | fi 131 | if [ "$line" == "$headerEnd" ]; 132 | then 133 | isInHeader=0 134 | fi 135 | done < $fileName 136 | done 137 | 138 | if [ $sourcesLineIndex -ne -1 ]; 139 | then 140 | # insert list of source files (each in new line) 141 | sourcesLines="${output[$sourcesLineIndex-1]}" 142 | for sourceFileName in "${sourceFiles[@]}"; 143 | do 144 | sourcesLines="$sourcesLines$sourceFileName\n" 145 | done 146 | output[$sourcesLineIndex-1]="$sourcesLines" 147 | fi 148 | 149 | output[${#output[@]}]="\n\n]]\n" 150 | 151 | if [ ! $footerFileName == "" ]; 152 | then 153 | if [ ! -e $footerFileName ]; 154 | then 155 | echo "File: $footerFileName does not exist." 156 | exit 157 | fi 158 | while read -r line; 159 | do 160 | output[${#output[@]}]="$line\n" 161 | done < $footerFileName 162 | fi 163 | 164 | # check whether we have permissions to write to a file 165 | touch $outputFileName 166 | if [ $? -ne 0 ] && [ ! -f $outputFileName ]; 167 | then 168 | echo "Cannot write to file: $outputFileName" 169 | exit 170 | fi 171 | 172 | # write output to a file 173 | rm $outputFileName 174 | for str in "${output[@]}"; do 175 | echo -e -n "$str" >> $outputFileName 176 | done 177 | -------------------------------------------------------------------------------- /ffi-gen.exports: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/violetav/lua-ffi-gen/ca45fb6abf0ce9115aecf268d81dd8972354833e/ffi-gen.exports --------------------------------------------------------------------------------