Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
691 lines (607 sloc) 21.6 KB
import llvm.libclang.*;
import printer.formatter.(repr);
import printer.(print,println,printTo,printlnTo,str);
import hashmaps.*;
import algorithms.(in?);
import io.files.*;
import strings.*;
import vectors.*;
import sequences.operators.*;
var clayKeywords = array(
"public", "private", "import", "as",
"record", "variant", "instance",
"define", "overload", "external", "alias",
"static", "rvalue",
"inline", "enum", "var", "ref", "forward",
"and", "or", "not",
"if", "else", "goto", "return", "while",
"switch", "case", "break", "continue", "for", "in",
"true", "false", "try", "catch", "throw",
"finally", "onerror",
"eval", "with",
"__FILE__", "__LINE__", "__COLUMN__", "__ARG__"
);
var g_declNames = HashMap[CXCursor, String]();
var g_unnamedDecl = HashMap[CXCursor, Bool]();
var g_unnamedIndex = 0;
var g_bindingsOut = stdoutFile();
var g_visitedSymbols = HashMap[String, Bool]();
overload printTo(stream, s:CXString) : {
printTo(stream, CStringRef(clang_getCString(s)));
}
clayKeyword?(s:String) : Bool = in?(clayKeywords, s);
clayIdentifier(s:String) : String {
if (clayKeyword?(s)) {
return str(s, "_");
} else {
return str(s);
}
}
unnamedName() : String {
inc(g_unnamedIndex);
return str("Unnamed",g_unnamedIndex);
}
nameForDeclaration(cursor:CXCursor) : String
{
var namep = lookup(g_declNames, cursor);
if (null?(namep)) {
var spelling = str(clang_getCursorSpelling(cursor));
var prefix = String();
switch (cursor.kind)
case (CXCursor_StructDecl)
prefix = String("Struct_");
case (CXCursor_UnionDecl)
prefix = String("Union_");
case (CXCursor_EnumDecl)
prefix = String("Enum_");
else
prefix = String("Other_");
var name = String();
if (empty?(spelling))
name = prefix ++ unnamedName();
else
name = prefix ++ spelling;
g_declNames[cursor] = name;
return move(name);
} else
return namep^;
}
convertPointerType(type:CXType, refCursor:CXCursor) : String {
switch (type.kind)
case (CXType_Void)
return str("OpaquePointer");
case (CXType_Unexposed, CXType_FunctionProto, CXType_FunctionNoProto) {
// XXX Function pointer types inside fields or parms show up as "unexposed"
// but we can still extract the return type and visit parms
// XXX Pointers to forward-declared structs also show up as "unexposed"
// XXX calling convention
var returnType = clang_getResultType(type);
var decl = clang_getTypeDeclaration(type);
if (returnType.kind != CXType_Invalid) {
var name = String();
var varargs = clang_isFunctionTypeVariadic(type) != 0;
switch (clang_getFunctionTypeCallingConv(type))
case (CXCallingConv_X86StdCall)
printTo(name, "StdCallCodePointer");
case (CXCallingConv_X86FastCall)
printTo(name, "FastCallCodePointer");
else if (varargs)
printTo(name, "VarArgsCCodePointer");
else
printTo(name, "CCodePointer");
printTo(name, "[[");
var numArgs = clang_getNumArgTypes(type);
assert(numArgs >= 0);
for (arg in range(UInt(numArgs))) {
if (arg > 0)
printTo(name, ", ");
printTo(name, convertType(clang_getArgType(type, arg), refCursor));
}
printTo(name,
"],[",
if (returnType.kind == CXType_Void) str("") else convertType(returnType, refCursor),
"]]");
return move(name);
} else if (decl.kind != CXCursor_NoDeclFound) {
return str("Pointer[", convertTypeFromDeclaration(decl), "]");
} else {
return str("OpaquePointer /* unknown ",
clang_getTypeKindSpelling(type.kind),
" referenced by ",
clang_getCursorKindSpelling(refCursor.kind),
" ",
clang_getCursorSpelling(refCursor),
" */");
}
}
case (CXType_Typedef) {
var decl = clang_getTypeDeclaration(type);
var typedefType = clang_getTypedefDeclUnderlyingType(decl);
if (inValues?(typedefType.kind, CXType_FunctionProto, CXType_FunctionNoProto))
return convertPointerType(typedefType, refCursor);
}
return str("Pointer[", convertType(type, refCursor), "]");
}
convertTypeFromDeclaration(declaration:CXCursor) : String {
switch (declaration.kind)
case (CXCursor_StructDecl, CXCursor_UnionDecl, CXCursor_EnumDecl)
return nameForDeclaration(declaration);
case (CXCursor_TypedefDecl)
return clayIdentifier(str(clang_getCursorSpelling(declaration)));
else
return str("Opaque /* unknown ",
clang_getCursorKindSpelling(clang_getCursorKind(declaration)), " ",
clang_getCursorSpelling(declaration), " */");
}
convertType(type:CXType, refCursor:CXCursor) : String {
switch (type.kind)
case (CXType_Bool)
return str("Bool");
case (CXType_SChar, CXType_Char_S)
return str("CChar");
case (CXType_UChar, CXType_Char_U)
return str("CUChar");
case (CXType_UShort)
return str("UShort");
case (CXType_UInt)
return str("UInt");
case (CXType_ULong)
return str("CULong");
case (CXType_ULongLong)
return str("UInt64");
case (CXType_UInt128)
return str("UInt128");
case (CXType_Short)
return str("Short");
case (CXType_Int)
return str("Int");
case (CXType_Long)
return str("CLong");
case (CXType_LongLong)
return str("Int64");
case (CXType_Int128)
return str("Int128");
case (CXType_Float)
return str("Float");
case (CXType_Double)
return str("Double");
case (CXType_LongDouble)
return str("LongDouble");
case (CXType_Complex) {
var elementType = clang_getElementType(type);
switch (elementType.kind)
case (CXType_Float)
return str("Complex32");
case (CXType_LongDouble)
return str("ComplexLongDouble");
case (CXType_Double)
return str("Complex64");
else
return str("Opaque /* unsupported complex type ",
clang_getTypeKindSpelling(elementType.kind), " */");
}
case (CXType_Pointer)
return convertPointerType(clang_getPointeeType(type), refCursor);
case (CXType_Record, CXType_Typedef, CXType_Unexposed, CXType_Enum) {
var typeCursor = clang_getTypeDeclaration(type);
return convertTypeFromDeclaration(typeCursor);
}
case (CXType_ConstantArray) {
return str("Array[",
convertType(clang_getArrayElementType(type), refCursor),
", ",
clang_getArraySize(type),
"]",
);
}
case (CXType_Vector) {
return str("Vec[",
convertType(clang_getElementType(type), refCursor),
", ",
clang_getNumElements(type),
"]",
);
}
return str("/* unknown kind ",
clang_getTypeKindSpelling(type.kind), " */ Opaque");
}
visitStruct(cursor:CXCursor, parent:CXCursor, data:CXClientData) : Int
{
ref unnamedFieldIndex = Pointer[Int](data)^;
assert(clang_getCursorKind(parent) == CXCursor_StructDecl);
var kind = clang_getCursorKind(cursor);
switch (kind)
case (CXCursor_FieldDecl) {
var type = clang_getCursorType(cursor);
var name = str(clang_getCursorSpelling(cursor));
if (empty?(name)) {
name = str("?padding", unnamedFieldIndex);
unnamedFieldIndex +: 1;
}
printlnTo(g_bindingsOut, " ", clayIdentifier(name), " : ",
convertType(type, cursor), ",");
}
else {}
return CXChildVisit_Continue;
}
visitUnion(cursor:CXCursor, parent:CXCursor, data:CXClientData) : Int
{
assert(clang_getCursorKind(parent) == CXCursor_UnionDecl);
var kind = clang_getCursorKind(cursor);
switch (kind)
case (CXCursor_FieldDecl) {
var type = clang_getCursorType(cursor);
printlnTo(g_bindingsOut, " /* ", clang_getCursorSpelling(cursor), " */ ", convertType(type, cursor), ",");
}
else {}
return CXChildVisit_Continue;
}
record FunctionDeclAttributes (asmLabel:Maybe[String]);
visitFunctionAttr(cursor:CXCursor, parent:CXCursor, data:CXClientData) : Int
{
ref attributes = Pointer[FunctionDeclAttributes](data)^;
switch (cursor.kind)
case (CXCursor_AsmLabelAttr)
attributes.asmLabel = Maybe(str(clang_getCursorSpelling(cursor)));
return CXChildVisit_Continue;
}
visitEnum(cursor:CXCursor, parent:CXCursor, data:CXClientData) : Int
{
switch (cursor.kind)
case (CXCursor_EnumConstantDecl) {
printlnTo(g_bindingsOut, "alias ", clang_getCursorSpelling(cursor), " = ",
convertType(clang_getEnumDeclIntegerType(parent), parent), "(",
clang_getEnumConstantDeclValue(cursor), ");");
}
else {
}
return CXChildVisit_Continue;
}
noDefinition?(definition:CXCursor)
= inValues?(definition.kind, CXCursor_NoDeclFound, CXCursor_InvalidFile);
opaqueDeclaration(decl:CXCursor) {
var name = nameForDeclaration(decl);
if (not symbolVisited(name))
printlnTo(g_bindingsOut, "alias ", name, " = Opaque;\n");
}
opaqueType(type:CXType) {
if (inValues?(type.kind, CXType_Record, CXType_Enum)) {
var decl = clang_getTypeDeclaration(type);
var definition = clang_getCursorDefinition(decl);
if (noDefinition?(definition))
opaqueDeclaration(decl);
}
}
withForwardDecl(cursor:CXCursor, data:CXClientData, f) {
var definition = clang_getCursorDefinition(cursor);
if (cursor == definition) {
var name = str(clang_getCursorSpelling(cursor));
if (not empty?(name))
f(cursor, nameForDeclaration(cursor), data);
else
if (null?(lookup(g_unnamedDecl, cursor)))
g_unnamedDecl[cursor] = true;
} else if (noDefinition?(definition))
opaqueDeclaration(cursor);
}
cursorMatchesPatterns(cursor:CXCursor, options:BindgenOptions) : Bool {
var file = CXFile();
clang_getSpellingLocation(clang_getCursorLocation(cursor),
@file, null(UInt), null(UInt), null(UInt));
// builtin definitions have a null file
if (null?(file)) {
return options.matchBuiltins;
}
if (empty?(options.matchPatterns))
return true;
var filename = str(clang_getFileName(file));
for (pattern in options.matchPatterns)
if (in?(filename, pattern))
return true;
return false;
}
symbolVisited(name:String) : Bool
{
if (not null?(lookup(g_visitedSymbols, name)))
return true;
g_visitedSymbols[name] = true;
return false;
}
structDefinition(cursor:CXCursor, name:String, data:CXClientData) : {
if (not symbolVisited(name)) {
var unnamedFieldIndex = 0;
printlnTo(g_bindingsOut, "record ", name, " (");
clang_visitChildren(cursor, CXCursorVisitor(visitStruct),
CXClientData(@unnamedFieldIndex));
printlnTo(g_bindingsOut, ");\n");
}
}
unionDefinition(cursor:CXCursor, name:String, data:CXClientData) : {
if (not symbolVisited(name)) {
printlnTo(g_bindingsOut, "alias ", name, " = Union[");
clang_visitChildren(cursor, CXCursorVisitor(visitUnion), data);
printlnTo(g_bindingsOut, "];\n");
}
}
enumDefinition(cursor:CXCursor, name:String, data:CXClientData) : {
if (not symbolVisited(name)) {
printlnTo(g_bindingsOut, "alias ", name, " = ",
convertType(clang_getEnumDeclIntegerType(cursor), cursor), ";");
clang_visitChildren(cursor, CXCursorVisitor(visitEnum), data);
}
}
visitToplevel(cursor:CXCursor, parent:CXCursor, data:CXClientData) : Int
{
ref options = Pointer[BindgenOptions](data)^;
if (not cursorMatchesPatterns(cursor, options))
return CXChildVisit_Continue;
var kind = clang_getCursorKind(cursor);
switch (kind)
case (CXCursor_StructDecl) {
withForwardDecl(cursor, data, structDefinition);
return CXChildVisit_Recurse;
}
case (CXCursor_UnionDecl) {
withForwardDecl(cursor, data, unionDefinition);
return CXChildVisit_Recurse;
}
case (CXCursor_EnumDecl) {
withForwardDecl(cursor, data, enumDefinition);
printlnTo(g_bindingsOut);
return CXChildVisit_Continue;
}
case (CXCursor_FunctionDecl) {
var name = str(clang_getCursorSpelling(cursor));
if (symbolVisited(name))
return CXChildVisit_Continue;
// XXX dllimport/dllexport
var attributes = FunctionDeclAttributes();
clang_visitChildren(cursor, CXCursorVisitor(visitFunctionAttr),
CXClientData(@attributes));
var type = clang_getCursorType(cursor);
printTo(g_bindingsOut, "external (");
switch (clang_getFunctionTypeCallingConv(type))
case (CXCallingConv_X86StdCall)
printTo(g_bindingsOut, "stdcall");
case (CXCallingConv_X86FastCall)
printTo(g_bindingsOut, "fastcall");
else
printTo(g_bindingsOut, "cdecl");
maybe(attributes.asmLabel): asmLabel -> {
printTo(g_bindingsOut, ", ", repr(asmLabel));
} :: () -> {
if (clayKeyword?(name))
printTo(g_bindingsOut, ", ", repr(name));
}
printTo(g_bindingsOut, ") ", clayIdentifier(name), "(");
var argCount = clang_getNumArgTypes(type);
assert(argCount >= 0);
for (arg in range(UInt(argCount))) {
if (arg > 0)
printTo(g_bindingsOut, ", ");
printTo(g_bindingsOut, "arg", arg, ":",
convertType(clang_getArgType(type, arg), cursor));
}
if (clang_isFunctionTypeVariadic(type) != 0) {
if (argCount > 0)
printTo(g_bindingsOut, ", ");
printTo(g_bindingsOut, "..");
}
printTo(g_bindingsOut, ") : ");
var returnType = clang_getCursorResultType(cursor);
if (returnType.kind != CXType_Void)
printTo(g_bindingsOut, convertType(returnType, cursor));
printlnTo(g_bindingsOut, ";\n");
return CXChildVisit_Continue;
}
case (CXCursor_VarDecl) {
var name = str(clang_getCursorSpelling(cursor));
if (symbolVisited(name))
return CXChildVisit_Continue;
if (clayKeyword?(name))
printlnTo(g_bindingsOut, "external (", repr(name), ")",
clayIdentifier(name), " : ",
convertType(clang_getCursorType(cursor), cursor), ";\n");
else
printlnTo(g_bindingsOut, "external ", name, " : ",
convertType(clang_getCursorType(cursor), cursor), ";\n");
return CXChildVisit_Continue;
}
case (CXCursor_TypedefDecl) {
var name = str(clang_getCursorSpelling(cursor));
var underlyingType = clang_getTypedefDeclUnderlyingType(cursor);
if (underlyingType.kind == CXType_Unexposed)
underlyingType = clang_getCanonicalType(underlyingType);
var decl = clang_getTypeDeclaration(underlyingType);
var typeId = clayIdentifier(name);
if (clang_isCursorDefinition(decl) != 0 and
empty?(str(clang_getCursorSpelling(decl)))) {
g_unnamedDecl[decl] = false;
switch (decl.kind)
case (CXCursor_StructDecl) {
structDefinition(decl, typeId, data);
return CXChildVisit_Recurse;
}
case (CXCursor_UnionDecl) {
unionDefinition(decl, typeId, data);
return CXChildVisit_Recurse;
}
case (CXCursor_EnumDecl) {
enumDefinition(decl, typeId, data);
printlnTo(g_bindingsOut);
return CXChildVisit_Continue;
}
}
if (symbolVisited(name))
return CXChildVisit_Continue;
printlnTo(g_bindingsOut, "alias ", typeId, " = ",
convertType(underlyingType, cursor), ";\n");
opaqueType(underlyingType);
return CXChildVisit_Continue;
}
case (CXCursor_FieldDecl) {
return CXChildVisit_Continue;
}
return CXChildVisit_Recurse;
}
visitUnnamedDecl(cursor:CXCursor, data:CXClientData) : {
var name = nameForDeclaration(cursor);
switch (cursor.kind)
case (CXCursor_StructDecl) {
structDefinition(cursor, name, data);
}
case (CXCursor_UnionDecl) {
unionDefinition(cursor, name, data);
}
case (CXCursor_EnumDecl) {
enumDefinition(cursor, name, data);
printlnTo(g_bindingsOut);
}
}
record Usage ();
record MissingOutputFileName ();
record MissingImportModuleName ();
record MissingMatchPattern ();
instance Exception (
Usage,
MissingOutputFileName,
MissingImportModuleName,
MissingMatchPattern
);
overload printTo(s, _:Usage) {
printlnTo(s, "Usage: clay-bindgen [clang options...] [bindgen options...] input.h");
printlnTo(s, "Bindgen options:");
printlnTo(s, " -o <output.clay> Write bindings to <output.clay> (default stdout)");
printlnTo(s, " -import <module> Prefix \"import <module>.*;\" to generated bindings");
printlnTo(s, " -match <name> Only output bindings for definitions from files");
printlnTo(s, " whose name contains <name>");
printlnTo(s, " If multiple -match options are provided, files");
printlnTo(s, " matching any rule are bound to.");
printlnTo(s, " -builtins Output bindings for builtin definitions");
printlnTo(s, " (for example __builtin_va_list)");
printlnTo(s, "Most clang options (such as -I and -D) are also supported.");
}
overload printTo(s, _:MissingOutputFileName) {
printlnTo(s, "no output file specified after -o option");
}
overload printTo(s, _:MissingImportModuleName) {
printlnTo(s, "no module name specified after -import option");
}
overload printTo(s, _:MissingMatchPattern) {
printlnTo(s, "no filename pattern specified after -match option");
}
record BindgenOptions (
outputFileName:Maybe[String],
imports:Vector[String],
matchPatterns:Vector[String],
matchBuiltins:Bool
);
overload BindgenOptions() = BindgenOptions(
nothing(String), Vector[String](), Vector[String](), false
);
parseOptions(argc, argv) : Int, Vector[Pointer[CChar]], BindgenOptions
{
var clang_argc = 0;
var clang_argv = Vector[Pointer[CChar]]();
var options = BindgenOptions();
var arg = 1;
while (arg < argc) {
switch (CStringRef(argv[arg]))
case ("-help", "--help", "/?") {
throw Usage();
}
case ("-o") {
if (arg + 1 >= argc)
throw MissingOutputFileName();
options.outputFileName = Maybe(String(argv[arg + 1]));
arg +: 2;
}
case ("-import") {
if (arg + 1 >= argc)
throw MissingImportModuleName();
push(options.imports, String(argv[arg + 1]));
arg +: 2;
}
case ("-match") {
if (arg + 1 >= argc)
throw MissingMatchPattern();
push(options.matchPatterns, String(argv[arg + 1]));
arg +: 2;
}
case ("-builtins") {
options.matchBuiltins = true;
arg +: 1;
}
case ("--") {
clang_argc +: argc - arg;
for (i in range(arg, argc))
push(clang_argv, argv[i]);
break;
}
else {
inc(clang_argc);
push(clang_argv, argv[arg]);
arg +: 1;
}
}
return clang_argc, move(clang_argv), move(options);
}
main(argc, argv)
{
var index = clang_createIndex(0, 1);
if (null?(index)) {
println("clang failed to create index");
return 1;
}
finally clang_disposeIndex(index);
var clang_argc = 0;
var clang_argv = Vector[Pointer[CChar]]();
var options = BindgenOptions();
try {
clang_argc, clang_argv, options = parseOptions(argc, argv);
maybe(options.outputFileName): outputFileName -> {
g_bindingsOut = File(outputFileName, CREATE);
}
} catch (ex:Usage) {
print(ex);
return 2;
} catch (ex) {
print(ex);
return 1;
}
var unit = clang_parseTranslationUnit(
index,
null(CChar),
begin(clang_argv),
clang_argc,
null(Struct_CXUnsavedFile),
0u,
0u,
);
if (null?(unit)) {
printlnTo(stderr, "no input files given");
print(Usage());
return 2;
}
finally clang_disposeTranslationUnit(unit);
var error? = false;
for (i in range(0u, clang_getNumDiagnostics(unit))) {
var diag = clang_getDiagnostic(unit, i);
printlnTo(stderr, clang_formatDiagnostic(diag,
clang_defaultDiagnosticDisplayOptions()));
if (clang_getDiagnosticSeverity(diag) >= CXDiagnostic_Error)
error? = true;
}
if (error?)
return 1;
printlnTo(g_bindingsOut, "// automatically generated by clay-bindgen");
for (imp in options.imports)
printlnTo(g_bindingsOut, "import ", imp, ".*;");
printlnTo(g_bindingsOut);
var cursor = clang_getTranslationUnitCursor(unit);
clang_visitChildren(cursor, CXCursorVisitor(visitToplevel), CXClientData(@options));
for (c, b in items(g_unnamedDecl))
if (b)
visitUnnamedDecl(c, CXClientData(@options));
return 0;
}
Jump to Line
Something went wrong with that request. Please try again.