├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cdefdb-helper ├── cdefdb-helper.lua ├── combine-cdefdbs ├── combine-cdefdbs.lua ├── examples ├── cat.lua └── hello.lua ├── functions.sh ├── gen-cdefdb ├── gen-cdefdb.lua ├── ljclang ├── COPYRIGHT.TXT ├── LLVM_LICENSE.TXT ├── Makefile ├── README.adoc ├── createheader.lua ├── extractdecls.lua ├── ljclang.lua ├── ljclang_Index_h.lua ├── ljclang_cursor_kind.lua ├── ljclang_support.c ├── mgrep.lua ├── parsecmdline_pk.lua └── test.lua ├── locally ├── share ├── cdef.lua └── cdefdb │ ├── cdefs.lua │ ├── open.lua │ └── write.lua └── test.lua /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.o 3 | /cdefdb.c 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014-2015 Brian Downing 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | llvm_incdir = /usr/include 2 | llvm_libdir = /usr/lib 3 | 4 | prefix = /usr 5 | 6 | bindir = $(prefix)/bin 7 | libdir = $(prefix)/lib 8 | datadir = $(prefix)/share 9 | 10 | lj_cdefdb_dir = $(libdir)/lj-cdefdb 11 | luashare_dir = $(datadir)/lua/5.1 12 | 13 | .PHONY: all 14 | all: ljclang 15 | 16 | .PHONY: ljclang 17 | ljclang: 18 | cd ljclang && make inc="$(llvm_incdir)" libdir="$(llvm_libdir)" 19 | 20 | BINS = cdefdb-helper gen-cdefdb combine-cdefdbs 21 | LIBS = functions.sh cdefdb-helper.lua gen-cdefdb.lua combine-cdefdbs.lua 22 | 23 | .PHONY: install 24 | install: all 25 | mkdir -p $(DESTDIR)$(luashare_dir) 26 | cp -r share/* $(DESTDIR)$(luashare_dir) 27 | echo "return { dir = '$(lj_cdefdb_dir)' }" \ 28 | > $(DESTDIR)$(luashare_dir)/cdefdb/config.lua 29 | mkdir -p $(DESTDIR)$(lj_cdefdb_dir) 30 | cp -r $(LIBS) $(DESTDIR)$(lj_cdefdb_dir) 31 | mkdir -p $(DESTDIR)$(bindir) 32 | cp -r $(BINS) $(DESTDIR)$(bindir) 33 | mkdir -p $(DESTDIR)$(lj_cdefdb_dir)/ljclang 34 | cp -r ljclang/ljclang*.lua ljclang/libljclang_support.so \ 35 | $(DESTDIR)$(lj_cdefdb_dir)/ljclang 36 | mkdir -p $(DESTDIR)$(lj_cdefdb_dir)/cdefdb.d 37 | mkdir -p $(DESTDIR)$(lj_cdefdb_dir)/stubs 38 | 39 | .PHONY: clean 40 | clean: 41 | cd ljclang && make clean 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | lj-cdefdb — An auto-generated cdef database for LuaJIT 2 | ====================================================== 3 | 4 | (The documentation on how to build/combine cdefdbs is out of date.) 5 | 6 | Introduction 7 | ------------ 8 | 9 | This is a "cdef database" for the LuaJIT API. It is automatically 10 | generated from a collection of header files. You can request specific 11 | cdefs (e.g. by function, type, or constant name) and it will load them 12 | (and only them) and all of their dependencies: 13 | 14 | ```lua 15 | local C, ffi = require 'cdef' { 16 | functions = { 'puts', 'printf', 'clock_gettime', 'open', 'fstat' }, 17 | constants = { 'O_RDONLY', 'CLOCK_*' } 18 | } 19 | 20 | local function ok(v) assert(v >= 0) return v end 21 | local function tsfloat(ts) 22 | return tonumber(ts.tv_sec) + tonumber(ts.tv_nsec) / 1000000000 23 | end 24 | 25 | C.puts('Hello, world!') 26 | local now = ffi.new('struct timespec') 27 | ok(C.clock_gettime(C.CLOCK_REALTIME, now)) 28 | local sbuf = ffi.new('struct stat') 29 | local fd = ok(C.open(arg[0], C.O_RDONLY)) 30 | ok(C.fstat(fd, sbuf)) 31 | C.printf('This script was last modified %f seconds ago.\n', 32 | ffi.cast('double', tsfloat(now) - tsfloat(sbuf.st_mtim))) 33 | ``` 34 | 35 | As an example of what gets loaded: 36 | 37 | ```c 38 | $ luajit -e "require 'cdef' { verbose = true, functions = 'clock_gettime', constants = 'CLOCK_REALTIME' }" 39 | local ffi = require 'ffi' 40 | ffi.cdef[==[ 41 | typedef long __kernel_long_t; 42 | typedef __kernel_long_t __kernel_time_t; 43 | struct timespec { 44 | __kernel_time_t tv_sec; 45 | long tv_nsec; 46 | }; 47 | typedef int __clockid_t; 48 | typedef __clockid_t clockid_t; 49 | extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __attribute__ ((__nothrow__ )); 50 | /* macro */ enum { CLOCK_REALTIME = 0 }; 51 | ]==] 52 | ``` 53 | 54 | lj-cdefdb keeps track of what has been loaded and will not load the 55 | same cdefs again in future calls. 56 | 57 | This is intended to be used in self-contained LuaJIT codebases to both 58 | make it easy to use low-level C APIs and avoid issues of incompatible 59 | redefinitions of cdefs from multiple locations. 60 | 61 | ### Advantages 62 | 63 | * cdefs are automatically generated from system header files. There's 64 | little or no manual writing of cdefs required. 65 | 66 | * Minimal overhead in both time and memory; only the necessary cdefs 67 | can be loaded, and the cdef database lives in a shared library so 68 | that it can be demand-paged and shared between multiple LuaJIT 69 | processes. 70 | 71 | * No run-time dependencies, and it's careful not to load any 72 | unprefixed cdefs of its own, so your initial FFI namespace will be 73 | completely clean. 74 | 75 | * It's "just C"; reading the manpages and understanding the LuaJIT FFI 76 | is all you need to know to use it — there's no "luafied" interface 77 | you additionally need to learn. 78 | 79 | * You get the real header file definitions for your platform. This 80 | ensures there's no architecture-specific (or other) errors in cdefs. 81 | Also if lj-cdefdb is used consistently there's no danger of 82 | "clashing" definitions which can happen with hand-written cdefs from 83 | different packages that are used together. 84 | 85 | ### Disadvantages 86 | 87 | * There's only one database, so all header files for anything that 88 | might run need to have been built into that database. This probably 89 | makes this unsuitable for general system packaging, but it can work 90 | fine for a restricted codebase (e.g. a embedded device or a 91 | self-contained application). 92 | 93 | * It's "just C"; there's no helpful Lua shims on top of anything a la 94 | [ljsyscall](https://github.com/justincormack/ljsyscall). 95 | 96 | * You get the real header file definitions for your platform. This 97 | means they're likely FFI-definition incompatible with other 98 | FFI-using packages (again, such as ljsyscall). 99 | 100 | * It's brand new and quite possibly buggy. 101 | 102 | * It probably only works on Linux for now, and it probably needs a 103 | little bit of scripting/build-system work to be able to be 104 | cross-compiled, though that is a goal. 105 | 106 | Prerequisites 107 | ------------- 108 | 109 | lj-cdefdb uses [Clang](http://clang.llvm.org/) as its C parser. This 110 | will need to be installed to generate the cdef database. (Clang is 111 | _not_ required at runtime.) Examples of installing it for some 112 | distributions: 113 | 114 | * Debian derivatives: `# apt-get install clang-3.5 libclang-3.5-dev` 115 | * Fedora: `# yum install clang clang-devel` 116 | * Arch: `# pacman -S clang` 117 | * Gentoo: `# emerge clang` 118 | 119 | Also obviously LuaJIT must be installed. 120 | 121 | Database Generation 122 | ------------------- 123 | 124 | The `gen-cdefdb` command is used to generate the database. It takes 125 | as input a C file that should include every header file whose 126 | definitions are required in the database. The usage of this is: 127 | 128 | ``` 129 | ./gen-cdefdb (file.c|-) 130 | ``` 131 | 132 | This generates `cdefdb.so` (and `cdefdb.c`) in the current directory. 133 | 134 | Usage 135 | ----- 136 | 137 | Copy `cdef.lua` to your lua share directory and `cdefdb.so` to your 138 | lua lib directory. Then you can do, for example: 139 | 140 | ```lua 141 | require 'cdef' { 142 | functions = { 'open', 'read', 'write', 'close' }, 143 | constants = { 'O_RDONLY' }, 144 | } 145 | ``` 146 | ...which will import the requested cdefs, and all of their 147 | dependencies, into the LuaJIT FFI C namespace. 148 | 149 | In addition to `functions` and `constants`, you can also request 150 | `variables`, `structs`, `unions`, `enums`, and `typedefs`. (Structs, 151 | unions, and enums should not include `struct`, `union`, or `enum` in 152 | their names.) Also, by adding `verbose = true`, it will print out the 153 | cdefs that are being loaded. You need not include types that are 154 | arguments of loaded function calls or contained by other types, as 155 | they will be pulled in automatically. Note however that a `typedef` 156 | for a loaded named `struct` will not itself be loaded unless it is 157 | referenced by something else or specifically requested, even if they 158 | were defined in a single statement in the header files! 159 | 160 | If there is only one of a kind requested a string can be used instead 161 | of a single-entry table. 162 | 163 | Finally, a glob-style star can be used at the end (and only the end) 164 | of a name: 165 | 166 | ```lua 167 | require 'cdef' { constants = 'O_*' } 168 | ``` 169 | 170 | `require 'cdef'` always returns `ffi.C` and `ffi`, so you can save 171 | some typing in common cases: 172 | 173 | ```lua 174 | local C, ffi = require 'cdef' { ... } 175 | ``` 176 | 177 | Stability 178 | --------- 179 | 180 | The `require 'cdef'` interface is unlikely to change incompatibly at 181 | this point. The database format, however, is not (and likely never 182 | will be) stable and must be regerated each time lj-cdefdb is updated. 183 | 184 | cdef Helper Utility 185 | ------------------- 186 | 187 | `cdef-helper` is a tool to help convert `ffi.cdef` statements (as may 188 | be seen in existing code) into `require 'cdef'` statements. Its usage 189 | is: 190 | 191 | ``` 192 | ./cdef-helper (cdef_bodies.c|-) 193 | ``` 194 | 195 | Its input should consist of the bodies of the ffi.cdef you'd like to 196 | convert. For example: 197 | 198 | ``` 199 | $ cat /tmp/foo.c 200 | void *malloc(size_t sz); 201 | void *realloc(void*ptr, size_t size); 202 | void free(void *ptr); 203 | int sprintf(char *str, const char *format, ...); 204 | int printf(const char *format, ...); 205 | $ ./cdef-helper /tmp/foo.c 206 | require 'cdef' { 207 | functions = { 208 | 'free', 209 | 'malloc', 210 | 'printf', 211 | 'realloc', 212 | 'sprintf', 213 | }, 214 | } 215 | ``` 216 | 217 | Additionally, you can add `__emit__();` in the input file to cause a 218 | `require 'cdef'` statement to be emitted immediately; this is useful 219 | for files that have multiple sequential `ffi.cdef` statements mixed 220 | with other code: 221 | 222 | ``` 223 | $ cat /tmp/foo.c 224 | void *malloc(size_t sz); 225 | void *realloc(void*ptr, size_t size); 226 | void free(void *ptr); 227 | __emit__(); 228 | int socket(int domain, int type, int protocol); 229 | int bind(int fd, const struct sockaddr *addr, socklen_t len); 230 | int listen(int fd, int backlog); 231 | $ ./cdef-helper /tmp/foo.c 232 | require 'cdef' { 233 | functions = { 234 | 'free', 235 | 'malloc', 236 | 'realloc', 237 | }, 238 | } 239 | require 'cdef' { 240 | functions = { 241 | 'bind', 242 | 'listen', 243 | 'socket', 244 | }, 245 | } 246 | ``` 247 | 248 | Thanks 249 | ------ 250 | 251 | Thanks to Philipp Kutin for [ljclang](https://github.com/helixhorned/ljclang). 252 | 253 | Copyright and License 254 | --------------------- 255 | 256 | Copyright © 2014–2015 [Brian Downing](https://github.com/bdowning). 257 | [MIT License.](LICENSE) 258 | -------------------------------------------------------------------------------- /cdefdb-helper: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (C) 2014-2015 Brian Downing. MIT License. 4 | 5 | set -e 6 | 7 | if [ -z "$1" ]; then 8 | echo Usage: $0 '(cdef_bodies.c|-)' '' >&2 9 | exit 1 10 | fi 11 | 12 | lj_cdefdb_dir=${LJ_CDEFDB_DIR:-$(luajit -e "print(require('cdefdb.config').dir)")} 13 | 14 | file="$1"; shift 15 | . "$lj_cdefdb_dir"/functions.sh 16 | 17 | infile="/tmp/lj-cdefdb-helper$$.c" 18 | trap "rm -f $infile" 0 19 | 20 | cat >"$infile" <>"$infile" 38 | 39 | run_in_ljclang ../cdefdb-helper.lua "$infile" "$@" 40 | -------------------------------------------------------------------------------- /cdefdb-helper.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | 3 | -- Copyright (C) 2014-2015 Brian Downing. MIT License. 4 | 5 | local arg = arg 6 | 7 | assert(arg[1], "Usage: "..arg[0].." ...") 8 | 9 | local cl = require("ljclang") 10 | 11 | arg[0] = nil 12 | local tu = cl.createIndex():parse(arg) 13 | 14 | if (tu == nil) then 15 | print('TU is nil') 16 | os.exit(1) 17 | end 18 | 19 | local predefined = { 20 | __gnuc_va_list = true, 21 | va_list = true, 22 | ptrdiff_t = true, 23 | size_t = true, 24 | wchar_t = true, 25 | int8_t = true, 26 | int16_t = true, 27 | int32_t = true, 28 | int64_t = true, 29 | uint8_t = true, 30 | uint16_t = true, 31 | uint32_t = true, 32 | uint64_t = true, 33 | intptr_t = true, 34 | uintptr_t = true, 35 | } 36 | 37 | local syms = { 38 | functions = { }, 39 | variables = { }, 40 | structs = { }, 41 | unions = { }, 42 | typedefs = { }, 43 | constants = { }, 44 | unemitted = 0 45 | } 46 | 47 | local function emit_foo(kind) 48 | local to_emit = { } 49 | for sym, state in pairs(syms[kind]) do 50 | if state then 51 | table.insert(to_emit, sym) 52 | syms[kind][sym] = false 53 | end 54 | end 55 | table.sort(to_emit) 56 | if #to_emit > 0 then 57 | io.stdout:write(' '..kind..' = {\n') 58 | for _, sym in ipairs(to_emit) do 59 | io.stdout:write(" '"..sym.."',\n") 60 | end 61 | io.stdout:write(' },\n') 62 | end 63 | end 64 | local function emit() 65 | if syms.unemitted > 0 then 66 | io.stdout:write("require 'cdef' {\n") 67 | emit_foo('functions') 68 | emit_foo('variables') 69 | emit_foo('structs') 70 | emit_foo('unions') 71 | emit_foo('typedefs') 72 | emit_foo('constants') 73 | io.stdout:write("}\n") 74 | syms.unemitted = 0 75 | end 76 | end 77 | local function add_sym(kind, sym) 78 | if sym ~= '' and syms[kind][sym] == nil then 79 | syms[kind][sym] = true 80 | syms.unemitted = syms.unemitted + 1 81 | end 82 | end 83 | 84 | for _, stmt in ipairs(tu:cursor():children()) do 85 | if stmt:haskind("FunctionDecl") then 86 | if stmt:name() == '__emit__' then 87 | emit() 88 | else 89 | add_sym('functions', stmt:name()) 90 | end 91 | elseif stmt:haskind("VarDecl") then 92 | add_sym('variables', stmt:name()) 93 | elseif stmt:haskind("StructDecl") then 94 | add_sym('structs', stmt:name()) 95 | elseif stmt:haskind("UnionDecl") then 96 | add_sym('unions', stmt:name()) 97 | elseif stmt:haskind("TypedefDecl") then 98 | if not predefined[stmt:name()] then 99 | add_sym('typedefs', stmt:name()) 100 | end 101 | elseif stmt:haskind("EnumDecl") then 102 | for _, field in ipairs(stmt:children()) do 103 | if field:haskind("EnumConstantDecl") then 104 | add_sym('constants', field:name()) 105 | end 106 | end 107 | else 108 | print('-- unknown', stmt:kind(), stmt:name()) 109 | end 110 | end 111 | 112 | emit() 113 | -------------------------------------------------------------------------------- /combine-cdefdbs: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (C) 2014-2015 Brian Downing. MIT License. 4 | 5 | set -e 6 | 7 | lj_cdefdb_dir=${LJ_CDEFDB_DIR:-$(luajit -e "print(require('cdefdb.config').dir)")} 8 | 9 | usage() { 10 | echo "Usage: $0 [options] " >&2 11 | echo " -o FILE, --output=FILE set output file" >&2 12 | echo " (default '$lj_cdefdb_dir/cdefdb')" >&2 13 | exit $1 14 | } 15 | 16 | OPTS=$(getopt -o ho: -l help,output-dir: -n "$0" -- "$@") 17 | eval set -- "$OPTS" 18 | 19 | out="$lj_cdefdb_dir/cdefdb" 20 | while true; do 21 | case "$1" in 22 | -h|--help) usage 0; shift;; 23 | -o|--output-dir) out=$2; shift 2;; 24 | --) shift; break;; 25 | *) echo "Option parse error!"; exit 1;; 26 | esac 27 | done 28 | 29 | . "$lj_cdefdb_dir"/functions.sh 30 | 31 | luajit "$lj_cdefdb_dir/combine-cdefdbs.lua" "$out" "$@" 32 | -------------------------------------------------------------------------------- /combine-cdefdbs.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2014-2015 Brian Downing. MIT License. 2 | 3 | local ffi = require 'ffi' 4 | 5 | local cdef, C, ffi_string = 6 | ffi.cdef, ffi.C, ffi.string 7 | 8 | local dbg = function () end 9 | -- dbg = print 10 | 11 | local cdefdb_open = require 'cdefdb.open' 12 | local cdefdb_write = require 'cdefdb.write' 13 | 14 | local db_names = {...} 15 | local outfile = table.remove(db_names, 1) 16 | assert(outfile) 17 | 18 | local dbs = { } 19 | for _, name in ipairs(db_names) do 20 | dbs[name] = cdefdb_open(name) 21 | end 22 | 23 | table.sort(db_names, function (a, b) 24 | if dbs[a].header.priority == dbs[b].header.priority then 25 | return dbs[a].size > dbs[b].size 26 | end 27 | return dbs[a].header.priority > dbs[b].header.priority 28 | end) 29 | 30 | local function get_string(db, offset) 31 | return ffi_string(db.strings + offset) 32 | end 33 | 34 | local stmt_idx = 1 35 | local stmts = { } 36 | local constants = { } 37 | 38 | local function get_tag(db, stmt_idx) 39 | local db_stmt = db.stmts[stmt_idx] 40 | local name = get_string(db, db_stmt.name) 41 | local kind = get_string(db, db_stmt.kind) 42 | if name == '' then 43 | name = get_string(db, db_stmt.extent):gsub('%s*', '') 44 | end 45 | return kind..','..name 46 | end 47 | 48 | for _, db_name in ipairs(db_names) do 49 | local db = dbs[db_name] 50 | for i = 0, db.header.num_stmts - 1 do 51 | local db_stmt = db.stmts[i] 52 | local stmt = { 53 | name = get_string(db, db_stmt.name), 54 | kind = get_string(db, db_stmt.kind), 55 | extent = get_string(db, db_stmt.extent), 56 | file = get_string(db, db_stmt.file), 57 | tag = get_tag(db, i), 58 | idx = stmt_idx, 59 | deps = { }, 60 | delayed_deps = { }, 61 | db_name = db_name 62 | } 63 | for _, dep_type in ipairs{'deps', 'delayed_deps'} do 64 | local deps = { } 65 | local d = db_stmt[dep_type] 66 | while db.stmt_deps[d] ~= -1 do 67 | stmt[dep_type][get_tag(db, db.stmt_deps[d])] = true 68 | d = d + 1 69 | end 70 | end 71 | if not stmts[stmt.tag] or stmts[stmt.tag].db_name == db_name then 72 | -- print('name', stmt.name) 73 | -- print('kind', stmt.kind) 74 | -- print('extent', stmt.extent) 75 | -- print('file', stmt.file) 76 | -- print('db_name', stmt.db_name) 77 | -- print('deps', db_stmt.deps) 78 | -- for dep, _ in pairs(stmt.deps) do 79 | -- print('', dep) 80 | -- end 81 | -- print('delayed_deps', db_stmt.delayed_deps) 82 | -- for dep, _ in pairs(stmt.delayed_deps) do 83 | -- print('', dep) 84 | -- end 85 | if stmts[stmt.tag] and stmts[stmt.tag].db_name == db_name then 86 | stmt.idx = stmts[stmt.tag].idx 87 | else 88 | stmt_idx = stmt_idx + 1 89 | end 90 | stmts[stmt.tag] = stmt 91 | end 92 | end 93 | 94 | for i = 0, db.header.num_constants - 1 do 95 | local name = get_string(db, db.constants_idx[i].name) 96 | if not constants[name] then 97 | constants[name] = stmts[get_tag(db, db.constants_idx[i].stmt)] 98 | end 99 | end 100 | end 101 | 102 | local f = assert(io.open(outfile, 'w')) 103 | cdefdb_write(f, stmts, constants) 104 | f:close() 105 | -------------------------------------------------------------------------------- /examples/cat.lua: -------------------------------------------------------------------------------- 1 | local ffi = require 'ffi' 2 | local C = ffi.C 3 | 4 | require 'cdef' { 5 | functions = { 'open', 'read', 'write', 'close' }, 6 | constants = { 'O_RDONLY' }, 7 | } 8 | 9 | local fd = C.open(arg[1], C.O_RDONLY) 10 | assert(fd >= 0) 11 | local buf = ffi.new('char[512]') 12 | repeat 13 | local r = C.read(fd, buf, ffi.sizeof(buf)) 14 | if r > 0 then 15 | C.write(1, buf, r) 16 | end 17 | until r <= 0 18 | C.close(fd) 19 | -------------------------------------------------------------------------------- /examples/hello.lua: -------------------------------------------------------------------------------- 1 | local C, ffi = require 'cdef' { 2 | functions = { 'puts', 'printf', 'clock_gettime', 'open', 'fstat' }, 3 | constants = { 'O_RDONLY', 'CLOCK_*' } 4 | } 5 | 6 | local function ok(v) assert(v >= 0) return v end 7 | local function tsfloat(ts) 8 | return tonumber(ts.tv_sec) + tonumber(ts.tv_nsec) / 1000000000 9 | end 10 | 11 | C.puts('Hello, world!') 12 | local now = ffi.new('struct timespec') 13 | ok(C.clock_gettime(C.CLOCK_REALTIME, now)) 14 | local sbuf = ffi.new('struct stat') 15 | local fd = ok(C.open(arg[0], C.O_RDONLY)) 16 | ok(C.fstat(fd, sbuf)) 17 | C.printf('This script was last modified %f seconds ago.\n', 18 | ffi.cast('double', tsfloat(now) - tsfloat(sbuf.st_mtim))) 19 | -------------------------------------------------------------------------------- /functions.sh: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2014-2015 Brian Downing. MIT License. 2 | 3 | lj_cdefdb_dir=${LJ_CDEFDB_DIR:-$(luajit -e "print(require('cdefdb.config').dir)")} 4 | 5 | prefixes="/usr/lib/llvm-3.4 /usr/lib/llvm-3.5 /usr /usr/local" 6 | llvmlib=/usr/lib 7 | for d in $prefixes; do 8 | for p in lib64/llvm lib/llvm lib64 lib; do 9 | if [ -e "$d"/"$p"/libclang.so ]; then 10 | llvmlib="$d/$p" 11 | fi 12 | done 13 | done 14 | 15 | run_in_ljclang() ( 16 | cd "$lj_cdefdb_dir/ljclang" 17 | LD_LIBRARY_PATH=.:"$llvmlib":${LD_LIBRARY_PATH:-.} luajit "$@" 18 | ) 19 | -------------------------------------------------------------------------------- /gen-cdefdb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Copyright (C) 2014-2015 Brian Downing. MIT License. 4 | 5 | set -e 6 | 7 | lj_cdefdb_dir=${LJ_CDEFDB_DIR:-$(luajit -e "print(require('cdefdb.config').dir)")} 8 | 9 | usage() { 10 | echo "Usage: $0 [options] (file.c|-) -- " >&2 11 | echo " -o DIR, --output-dir=DIR set output directory" >&2 12 | echo " (default 'lj-cdefdb-out')" >&2 13 | echo " -p PRIO, --priority=PRIO set priority for generated database" >&2 14 | echo " (default 0)" >&2 15 | exit $1 16 | } 17 | 18 | OPTS=$(getopt -o ho:p: -l help,output-dir:,priority: -n "$0" -- "$@") 19 | eval set -- "$OPTS" 20 | 21 | out=lj-cdefdb-out 22 | prio=0 23 | while true; do 24 | case "$1" in 25 | -h|--help) usage 0; shift;; 26 | -o|--output-dir) out=$2; shift 2;; 27 | -p|--priority) prio=$2; shift 2;; 28 | --) shift; break;; 29 | *) echo "Option parse error!"; exit 1;; 30 | esac 31 | done 32 | 33 | if [ -z "$1" ]; then 34 | echo "$0: No input file specified" >&2 35 | echo "Try '$0 --help' for more information." >&2 36 | exit 1 37 | fi 38 | 39 | file="$1"; shift 40 | . "$lj_cdefdb_dir"/functions.sh 41 | 42 | out=$(readlink -f "$out") 43 | 44 | pp_tmpdir="/tmp/lj-cdefdb-$$" 45 | mkdir -p "$pp_tmpdir" 46 | trap "rm -rf $pp_tmpdir" 0 47 | pp_output="$pp_tmpdir/preprocessed$$.c" 48 | ${CC:-cc} -E -dD "$@" "$file" > "$pp_output" 49 | 50 | hash=$(sha1sum "$pp_output" | cut -b1-8) 51 | 52 | mkdir -p "$out" 53 | run_in_ljclang ../gen-cdefdb.lua "$pp_output" "$out" "$hash" "$prio" 54 | 55 | mkdir -p "$out"/cdefdb.d 56 | mv "$out"/"$hash".db "$out"/cdefdb.d 57 | if [ -e "$out"/"$hash".c ]; then 58 | mkdir -p "$out"/stubs 59 | ${CC:-cc} -fPIC -shared -O2 -o "$out"/stubs/"$hash".so "$out"/"$hash".c 60 | rm "$out"/"$hash".c 61 | fi 62 | -------------------------------------------------------------------------------- /gen-cdefdb.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | 3 | -- Copyright (C) 2014-2015 Brian Downing. MIT License. 4 | 5 | local transparent_union_blacklist = { 6 | -- glibc __WAIT_STATUS (old 4.1BSD union wait * is first) 7 | 'union%s+wait%s+%*', 8 | } 9 | 10 | local libc_nonshared_functions = { 11 | { fn = 'atexit', 12 | headers = { 'stdlib.h' } }, 13 | { fn = 'stat', 14 | if_exists = { 'FunctionDecl,__xstat' }, 15 | headers = { 'sys/types.h', 'sys/stat.h', 'unistd.h' } }, 16 | { fn = 'fstat', 17 | if_exists = { 'FunctionDecl,__fxstat' }, 18 | headers = { 'sys/types.h', 'sys/stat.h', 'unistd.h' } }, 19 | { fn = 'lstat', 20 | if_exists = { 'FunctionDecl,__lxstat' }, 21 | headers = { 'sys/types.h', 'sys/stat.h', 'unistd.h' } }, 22 | { fn = 'stat64', 23 | fixups = { '#define _LARGEFILE64_SOURCE' }, 24 | if_exists = { 'FunctionDecl,__xstat64', 'StructDecl,stat64' }, 25 | headers = { 'sys/types.h', 'sys/stat.h', 'unistd.h' } }, 26 | { fn = 'fstat64', 27 | if_exists = { 'FunctionDecl,__fxstat64', 'StructDecl,stat64' }, 28 | fixups = { '#define _LARGEFILE64_SOURCE' }, 29 | headers = { 'sys/types.h', 'sys/stat.h', 'unistd.h' } }, 30 | { fn = 'lstat64', 31 | if_exists = { 'FunctionDecl,__lxstat64', 'StructDecl,stat64' }, 32 | fixups = { '#define _LARGEFILE64_SOURCE' }, 33 | headers = { 'sys/types.h', 'sys/stat.h', 'unistd.h' } }, 34 | { fn = 'fstatat', 35 | if_exists = { 'FunctionDecl,__fxstatat' }, 36 | headers = { 'sys/types.h', 'sys/stat.h', 'fcntl.h', 'unistd.h' } }, 37 | { fn = 'fstatat64', 38 | if_exists = { 'FunctionDecl,__fxstatat64', 'StructDecl,stat64' }, 39 | fixups = { '#define _LARGEFILE64_SOURCE' }, 40 | headers = { 'sys/types.h', 'sys/stat.h', 'fcntl.h', 'unistd.h' } }, 41 | { fn = 'mknod', 42 | if_exists = { 'FunctionDecl,__xmknod' }, 43 | headers = { 'sys/types.h', 'sys/stat.h', 'fcntl.h', 'unistd.h' } }, 44 | { fn = 'mknodat', 45 | if_exists = { 'FunctionDecl,__xmknodat' }, 46 | headers = { 'sys/types.h', 'sys/stat.h', 'fcntl.h', 'unistd.h' } }, 47 | } 48 | 49 | local function tmap(t, f) 50 | local r = { } 51 | for i = 1, #t do 52 | r[#r+1] = f(t[i]) 53 | end 54 | return r 55 | end 56 | 57 | local function tappend(r, ...) 58 | local ts = {...} 59 | for _, t in ipairs(ts) do 60 | for i = 1, #t do 61 | r[#r+1] = t[i] 62 | end 63 | end 64 | return r 65 | end 66 | 67 | local function tjoin(...) 68 | return tappend({ }, ...) 69 | end 70 | 71 | local function noprint() end 72 | local function errprint(...) 73 | io.stderr:write(table.concat(tmap({...}, tostring), ' ')..'\n') 74 | end 75 | local function errprintf(...) 76 | io.stderr:write(string.format(...)) 77 | end 78 | local dbg = noprint 79 | -- dbg = errprint 80 | 81 | local ppfile, outdir, hash, priority = ... 82 | 83 | assert(ppfile and outdir, "Usage: "..arg[0].." ") 84 | 85 | local cl = require("ljclang") 86 | 87 | local tu = cl.createIndex():parse({ppfile}, {"DetailedPreprocessingRecord"}) 88 | 89 | if (tu == nil) then 90 | print('TU is nil') 91 | os.exit(1) 92 | end 93 | 94 | local tu_cur = tu:cursor() 95 | 96 | do 97 | local cacheF = setmetatable({}, {__mode="k"}) 98 | function getExtent(file, fromOffs, toOffs) 99 | if not file then return '' end 100 | local f = cacheF[file] 101 | if not f then 102 | f = assert(io.open(file)) 103 | cacheF[file] = f 104 | end 105 | f:seek('set', fromOffs) 106 | local r = f:read(toOffs - fromOffs) 107 | return r 108 | end 109 | end 110 | 111 | local function cursor_tag(cur) 112 | -- local f, b, e = cur:location('offset') 113 | -- return string.format('%s:%d:%d', f or '?', b or 0, e or 0) 114 | local f, r1, c1, r2, c2 = cur:location() 115 | return string.format('%s:%d:%d:%d:%d', f or '?', r1 or 0, c1 or 0, r2 or 0, c2 or 0) 116 | end 117 | 118 | local function children_attrs(cur) 119 | local non_attrs, attrs = { }, { } 120 | for _, kid in ipairs(cur:children()) do 121 | if kid:kind():match('Attr$') then 122 | table.insert(attrs, kid) 123 | else 124 | table.insert(non_attrs, kid) 125 | end 126 | end 127 | return non_attrs, attrs 128 | end 129 | 130 | local function struct_fields(cur) 131 | local fields = { } 132 | for i, kid in ipairs(cur:children()) do 133 | if kid:haskind('FieldDecl') then 134 | -- dbg(' FieldDecl', kit) 135 | table.insert(fields, kid) 136 | end 137 | end 138 | return fields 139 | end 140 | 141 | function base_type(type) 142 | if type:haskind('ConstantArray') or type:haskind('VariableArray') then 143 | return base_type(type:arrayElementType()) 144 | elseif type:haskind('Pointer') then 145 | return base_type(type:pointee()) 146 | else 147 | return type 148 | end 149 | end 150 | 151 | function is_pointer(type) 152 | if type:haskind('ConstantArray') or type:haskind('VariableArray') then 153 | return is_pointer(type:arrayElementType()) 154 | elseif type:haskind('Pointer') then 155 | return true 156 | else 157 | return false 158 | end 159 | end 160 | 161 | function strip_hashes(str) 162 | repeat 163 | local old = str 164 | str = old:gsub('\n#[^\n]*\n', '\n') 165 | until old == str 166 | return str 167 | end 168 | 169 | local redef_tag = '__LJ_CDEFDB_REDEFINED__' 170 | 171 | local stmts = { } 172 | local stmt_idx = 1 173 | local kind_name_map = { } 174 | 175 | local struct_dep_mode = 'delayed_deps' 176 | local typedef_ends = { } 177 | local enums = { } 178 | local macros = { } 179 | local tags_by_kind = { } 180 | 181 | function haskind_structish(decl) 182 | return decl:haskind('StructDecl') or decl:haskind('UnionDecl') 183 | end 184 | 185 | function store_stmt(cur) 186 | if not cur:location() then return end 187 | 188 | if haskind_structish(cur) then 189 | cur = cur:type():declaration() 190 | end 191 | 192 | local tag = cursor_tag(cur) 193 | if stmts[tag] then return end 194 | 195 | local realfile, rr1, rc1, _, _, b, e = cur:location() 196 | local file, pr1, pc1 = cur:presumedLocation() 197 | if realfile == '???' then return end 198 | local stmt = { 199 | name = cur:name(), 200 | kind = cur:kind(), 201 | extent = strip_hashes(getExtent(cur:location('offset'))), 202 | file = file, 203 | pr1 = pr1, pc1 = pc1, 204 | tag = tag, 205 | deps = { }, 206 | delayed_deps = { }, 207 | no_deps = { }, 208 | idx = stmt_idx, 209 | outside_attrs = { }, 210 | cur = cur 211 | } 212 | 213 | local _, attrs = children_attrs(cur) 214 | for _, attr in ipairs(attrs) do 215 | local f, ab, ae = attr:location('offset') 216 | if not f then 217 | errprintf('Warning: Likely UNSUPPORTED pragma: %s "%s" %s:%d:%d\n', 218 | stmt.kind, stmt.name, file, pr1, pc1) 219 | else 220 | local attr_extent = strip_hashes(getExtent(f, ab, ae)) 221 | if attr_extent:match('transparent_union') then 222 | stmt.transparent_union = true 223 | else 224 | if ab < b or ae > e then 225 | table.insert(stmt.outside_attrs, attr_extent) 226 | end 227 | end 228 | end 229 | end 230 | 231 | if stmt.name:match(redef_tag) then 232 | stmt.name = stmt.name:gsub(redef_tag, '') 233 | stmt.extent = stmt.extent:gsub(redef_tag, '') 234 | end 235 | local redefined = false 236 | local orig_tag = nil 237 | if stmt.name ~= '' and not cur:haskind('MacroDefinition') then 238 | local kindname = stmt.kind..','..stmt.name 239 | if kind_name_map[kindname] then 240 | local old_tag = kind_name_map[kindname] 241 | local old_stmt = stmts[old_tag] 242 | if not cur:name():match(redef_tag) then 243 | errprintf('Warning: %s "%s" redefined:\n' .. 244 | ' Old %s:%d:%d\n' .. 245 | ' New %s:%d:%d\n', 246 | stmt.kind, stmt.name, 247 | old_stmt.file, old_stmt.pr1, old_stmt.pc1, 248 | file, pr1, pc1) 249 | end 250 | orig_tag = tag 251 | tag = old_tag 252 | stmt.tag = old_tag 253 | stmt.idx = old_stmt.idx 254 | if old_stmt.inner_structish then 255 | stmts[old_stmt.inner_structish.tag] = stmt 256 | end 257 | redefined = true 258 | end 259 | kind_name_map[kindname] = tag 260 | end 261 | 262 | if cur:haskind('MacroDefinition') then 263 | stmt.extent = stmt.extent:sub(#stmt.name + 1) 264 | if stmt.extent == ' '..stmt.name then 265 | dbg('ignore self-defined '..stmt.name, macros[stmt.name]) 266 | macros[stmt.name] = nil 267 | return 268 | end 269 | local _, tokens = cur:_tokens() 270 | table.remove(tokens, 1) 271 | stmt.tokens = tokens 272 | local params = stmt.extent:match('^%(([^)]*)%)') 273 | stmt.expansion = stmt.extent:match(' (.*)') 274 | if params then 275 | stmt.params = { } 276 | for p in params:gmatch('[^,]+') do 277 | table.insert(stmt.params, p) 278 | end 279 | local ps = #stmt.params 280 | local ptokens = 2 + (ps > 1 and ps * 2 - 1 or ps) 281 | for i = 1, ptokens do 282 | table.remove(stmt.tokens, 1) 283 | end 284 | end 285 | macros[stmt.name] = stmt.tag 286 | -- don't need more cleanup (more spaces, backslash-newlines) 287 | -- because clang -E -dD takes care of that 288 | end 289 | 290 | stmts[tag] = stmt 291 | if orig_tag then 292 | stmts[orig_tag] = stmt 293 | end 294 | if not redefined then 295 | stmt_idx = stmt_idx + 1 296 | end 297 | -- dbg('tag', tag) 298 | local by_kind = tags_by_kind[stmt.kind] or { } 299 | by_kind[stmt.name] = tag 300 | tags_by_kind[stmt.kind] = by_kind 301 | 302 | find_deps(cur, nil, struct_dep_mode, stmt) 303 | -- kill any self dependencies 304 | stmt.deps[stmt.tag] = nil 305 | stmt.delayed_deps[stmt.tag] = nil 306 | 307 | --dbg(tag, stmt.kind, stmt.name, stmt.tag, stmt.tag == tag) 308 | 309 | if cur:haskind('TypedefDecl') then 310 | -- deal with inline-defined structs 311 | local f, b, e = cur:location('offset') 312 | local td_starttag = f..','..b 313 | local td_basetype = base_type(cur:typedefType()) 314 | local decl = td_basetype:declaration() 315 | local _, kb, ke = decl:location('offset') 316 | local inner_stmt = stmts[cursor_tag(decl)] 317 | dbg('\ntypedef', f, b, kb, ke, e, decl:kind(), decl:name()) 318 | if inner_stmt and inner_stmt.transparent_union then 319 | local fields = struct_fields(decl) 320 | local override = fields[1] 321 | for _, field in ipairs(fields) do 322 | local extent = strip_hashes(getExtent(field:location('offset'))) 323 | local blacklisted = false 324 | for _, blacklist in ipairs(transparent_union_blacklist) do 325 | if extent:match(blacklist) then 326 | blacklisted = true 327 | end 328 | end 329 | if not blacklisted then 330 | override = field 331 | break 332 | end 333 | end 334 | local field_extent = strip_hashes(getExtent(override:location('offset'))) 335 | -- lop off the final identifier (field name) 336 | local field_name 337 | for id in field_extent:gmatch('[A-Za-z_][A-Za-z0-9_]*') do 338 | field_name = id 339 | end 340 | -- errprint('field_name = "'..field_name..'"') 341 | field_extent = field_extent:gsub('(.*)'..field_name, '%1') 342 | -- errprint('override', decl:name(), field_extent) 343 | stmt.extent = 'typedef '..field_extent..' '..stmt.name 344 | if inner_stmt.name == '' then 345 | stmt.idx = inner_stmt.idx 346 | stmt_idx = stmt_idx - 1 347 | stmt.inner_structish = inner_stmt 348 | stmt.outside_attrs = tappend(inner_stmt.outside_attrs, 349 | stmt.outside_attrs) 350 | inner_stmt.outside_attrs = { } 351 | stmts[inner_stmt.tag] = stmt 352 | end 353 | elseif haskind_structish(decl) and kb and b <= kb and e >= ke then 354 | dbg('\ntypedef', stmt.kind, 'inner', f, b, kb, ke, e, decl:name(), #struct_fields(decl)) 355 | local orig = getExtent(f, b, e) 356 | local pre = orig:sub(1, kb - b) 357 | local post = orig:sub(ke - b + 1, e - b) 358 | if decl:name() == '' or #struct_fields(decl) == 0 then 359 | -- eat anon or empty structs defined inside typedefs 360 | if typedef_ends[td_starttag] then 361 | error("UNSUPOPRTED: multiply-defined typedefs for " .. 362 | "anonymous structs (for typedef "..cur:name()..")!") 363 | end 364 | stmt.idx = inner_stmt.idx 365 | stmt_idx = stmt_idx - 1 366 | for k, v in pairs(inner_stmt.deps) do 367 | stmt.deps[k] = v 368 | end 369 | for k, v in pairs(inner_stmt.delayed_deps) do 370 | stmt.delayed_deps[k] = v 371 | end 372 | stmt.inner_structish = inner_stmt 373 | stmt.outside_attrs = tappend(inner_stmt.outside_attrs, 374 | stmt.outside_attrs) 375 | inner_stmt.outside_attrs = { } 376 | stmt.extent = strip_hashes(pre .. inner_stmt.extent .. post) 377 | stmts[inner_stmt.tag] = stmt 378 | else 379 | -- generate a new typedef referencing out the struct 380 | -- by name; this avoid several kinds of circular 381 | -- dependencies that are hard to work around otherwise 382 | local old_e = typedef_ends[td_starttag] 383 | if old_e then 384 | post = orig 385 | :sub(old_e - b + 1, e - b) 386 | :match('^%s*,%s*(%s.*)$') 387 | end 388 | stmt.extent = '/* generated */ ' .. strip_hashes(pre .. tostring(td_basetype) .. post) 389 | dbg('typedef', tag, 'decl', cursor_tag(decl), stmt.extent) 390 | end 391 | typedef_ends[td_starttag] = e 392 | end 393 | end 394 | end 395 | 396 | function find_deps(cur, parent, struct_ptr_mode, stmt) 397 | if cur:haskind('EnumConstantDecl') then 398 | dbg('EnumConstantDecl', cur:name()) 399 | enums[cur:name()] = stmt.tag 400 | end 401 | if cur:haskind('DeclRefExpr') then 402 | dbg('DeclRefExpr', cur:name()) 403 | if enums[cur:name()] then 404 | stmt.deps[enums[cur:name()]] = true 405 | else 406 | dbg(cur:name(), 'used before defined') 407 | end 408 | end 409 | if cur:haskind('ParmDecl') then 410 | -- structs in parameter lists are local, so they need to be 411 | -- defined (or at least forward-declared) first 412 | -- dbg('ParmDecl', cur, cursor_tag(cur)) 413 | for i, kid in ipairs(cur:children()) do 414 | find_deps(kid, cur, 'deps', stmt) 415 | end 416 | elseif cur:haskind('TypeRef') then 417 | local typedecl = cur:type():declaration() 418 | local mode = 'deps' 419 | local parent_type = parent:type() 420 | -- dbg(cur:type(), cur:type():declaration(), cursor_tag(cur:type():declaration())) 421 | if parent:haskind('FunctionDecl') then 422 | parent_type = parent:resultType() 423 | end 424 | if haskind_structish(typedecl) and is_pointer(parent_type) then 425 | mode = struct_ptr_mode 426 | end 427 | -- dbg(mode, cursor_tag(typedecl)) 428 | stmt[mode][cursor_tag(typedecl)] = true 429 | local canonical = cur:type():canonical() 430 | local parent_canonical = parent_type:canonical() 431 | -- dbg('CANONICAL', cur:type(), cur:type():canonical(), parent_type, parent_canonical) 432 | if haskind_structish(canonical:declaration()) and not is_pointer(parent_canonical) then 433 | -- dbg('CCCC') 434 | stmt.deps[cursor_tag(canonical:declaration())] = true 435 | end 436 | elseif cur:haskind('TypedefDecl') then 437 | local typedecl = base_type(cur:typedefType()):declaration() 438 | if haskind_structish(typedecl) then 439 | -- dbg('crawl (Struct/Union)Decl', typedecl) 440 | -- skip first child (the typeref), any attached structs 441 | -- are crawled extra above; otherwise we get an 442 | -- unneccessarily-strong dependency on the struct 443 | for i, kid in ipairs(children_attrs(cur)) do 444 | if i == 1 then 445 | stmt[struct_dep_mode][cursor_tag(typedecl)] = true 446 | else 447 | find_deps(kid, cur, struct_ptr_mode, stmt) 448 | end 449 | end 450 | else 451 | for i, kid in ipairs(cur:children()) do 452 | find_deps(kid, cur, struct_ptr_mode, stmt) 453 | end 454 | end 455 | else 456 | for i, kid in ipairs(cur:children()) do 457 | find_deps(kid, cur, struct_ptr_mode, stmt) 458 | end 459 | end 460 | end 461 | 462 | for _, cur in ipairs(tu_cur:children()) do 463 | if not cur:haskind('MacroExpansion') 464 | and not cur:haskind('InclusionDirective') 465 | and not (cur:haskind('FunctionDecl') and cur:isDefinition()) 466 | then 467 | store_stmt(cur) 468 | end 469 | end 470 | 471 | local consts = { } 472 | local test_num = 0 473 | local function const_test(token, indent) 474 | indent = indent or '' 475 | if consts[token] == 'testing' then 476 | dbg(indent..'const_test', token, 'recursed, therefore false') 477 | consts[token] = false 478 | return consts[token] 479 | end 480 | if consts[token] ~= nil then 481 | dbg(indent..'const_test', token, 'cached', not not consts[token]) 482 | return consts[token] 483 | end 484 | dbg(indent..'const_test', token) 485 | local tag = macros[token] 486 | if not tag then return consts[token] end 487 | local stmt = stmts[tag] 488 | consts[token] = 'testing' 489 | dbg(indent..' <'..stmt.extent..'>') 490 | if stmt.params then 491 | dbg(indent..' false due to unhandled params') 492 | consts[token] = false 493 | else 494 | local deps = { } 495 | local tokens = tmap(stmt.tokens, function(x) return x.extent end) 496 | for i, t in ipairs(tokens) do 497 | if macros[t] then 498 | table.insert(deps, macros[t]) 499 | end 500 | if enums[t] then 501 | table.insert(deps, enums[t]) 502 | dbg(indent..' is enum', t, 'replacing with 1') 503 | tokens[i] = '1' 504 | end 505 | const_test(t, indent..' ') 506 | if consts[t] == false then 507 | dbg(indent..' false due to non-const token', t) 508 | consts[token] = false 509 | return consts[token] 510 | end 511 | if consts[t] then 512 | tokens[i] = consts[t].test_sym 513 | end 514 | end 515 | test_num = test_num + 1 516 | local test_sym = string.format('__TEST_%d', test_num); 517 | local test = string.format('enum { %s = (%s) };', 518 | test_sym, table.concat(tokens, ' ')) 519 | dbg(indent..' test:', test) 520 | local is_const, err = pcall(function () 521 | require'ffi'.cdef(test) 522 | end) 523 | if is_const then 524 | dbg(indent..' true due to successful test') 525 | consts[token] = { deps = deps, test_sym = test_sym } 526 | else 527 | dbg(indent..' false due to failed test', err) 528 | consts[token] = false 529 | end 530 | end 531 | return consts[token] 532 | end 533 | 534 | for m, tag in pairs(macros) do 535 | local stmt = stmts[tag] 536 | local c = const_test(m) 537 | if c then 538 | for _, d in ipairs(c.deps) do 539 | stmt.deps[d] = true 540 | end 541 | end 542 | end 543 | 544 | if dbg ~= noprint then 545 | for tag, stmt in pairs(stmts) do 546 | print(stmt.idx, tag, stmt.kind, stmt.name, stmt.tag, stmt.tag == tag) 547 | for _, m in ipairs{'deps', 'delayed_deps'} do 548 | for t, _ in pairs(stmt[m]) do 549 | print('', m, t) 550 | end 551 | end 552 | end 553 | end 554 | 555 | stmts.StubRef = { 556 | name = hash, 557 | kind = 'StubRef', 558 | extent = '/* load cdefdb stubs '..hash..'.so */', 559 | tag = 'StubRef', 560 | deps = { }, 561 | delayed_deps = { }, 562 | no_deps = { }, 563 | idx = stmt_idx, 564 | outside_attrs = { }, 565 | file = '', 566 | } 567 | 568 | local fixups = { } 569 | local headers_included = { } 570 | for _, nonshared in ipairs(libc_nonshared_functions) do 571 | local fn_tag = tags_by_kind.FunctionDecl and tags_by_kind.FunctionDecl[nonshared.fn] 572 | if fn_tag then 573 | local stmt = stmts[fn_tag] 574 | local to_do = true 575 | for _, test in ipairs(nonshared.if_exists or { }) do 576 | local kind, name = test:match('(.*),(.*)') 577 | if not tags_by_kind[kind][name] then 578 | to_do = false 579 | break 580 | end 581 | end 582 | if to_do then 583 | local extent = stmt.extent 584 | extent = extent:gsub('^%s*extern%s*', '') 585 | extent = extent:gsub(stmt.name..'(%s*%()', 586 | 'cdefdb_'..hash..'_'..stmt.name..'%1') 587 | extent = extent:gsub('__attribute__.*', '') -- KLUDGE! 588 | for _, fixup in ipairs(nonshared.fixups or { }) do 589 | if not headers_included[fixup] then 590 | table.insert(fixups, 1, fixup) 591 | headers_included[fixup] = true 592 | end 593 | end 594 | for _, header in ipairs(nonshared.headers or { }) do 595 | if not headers_included[header] then 596 | table.insert(fixups, '#include <'..header..'>') 597 | headers_included[header] = true 598 | end 599 | end 600 | table.insert(fixups, extent) 601 | table.insert(fixups, '{') 602 | local args = { } 603 | for _, kid in ipairs(stmt.cur:children()) do 604 | if kid:haskind('ParmDecl') then 605 | table.insert(args, kid:name()) 606 | end 607 | end 608 | table.insert(fixups, string.format(' return %s(%s);', 609 | stmt.name, 610 | table.concat(args, ', '))) 611 | table.insert(fixups, '}') 612 | stmt.extent = stmt.extent 613 | .. ' asm("cdefdb_' .. hash .. '_' .. stmt.name .. '")' 614 | stmt.delayed_deps.StubRef = true 615 | end 616 | end 617 | end 618 | 619 | for _, stmt in pairs(stmts) do 620 | if #stmt.outside_attrs > 0 then 621 | stmt.extent = stmt.extent 622 | .. ' /* fabricated */ __attribute__ ((' 623 | .. table.concat(stmt.outside_attrs, ',') .. '))' 624 | end 625 | end 626 | 627 | local constants = { } 628 | for e, tag in pairs(enums) do 629 | constants[e] = stmts[tag] 630 | end 631 | for m, tag in pairs(macros) do 632 | if not constants[m] and consts[m] then 633 | constants[m] = stmts[tag] 634 | end 635 | end 636 | 637 | local ffi = require 'ffi' 638 | local cdefdb_write = require('cdefdb.write') 639 | 640 | local f = assert(io.open(outdir..'/'..hash..'.db', 'w')) 641 | cdefdb_write(f, stmts, constants, tonumber(priority)) 642 | f:close() 643 | 644 | if #fixups > 0 then 645 | local f = assert(io.open(outdir..'/'..hash..'.c', 'w')) 646 | f:write(table.concat(fixups, '\n')) 647 | f:write('\n') 648 | f:close() 649 | end 650 | -------------------------------------------------------------------------------- /ljclang/COPYRIGHT.TXT: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Philipp Kutin 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /ljclang/LLVM_LICENSE.TXT: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LLVM Release License 3 | ============================================================================== 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2003-2013 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 | llvm/projects/sample/autoconf 67 | Google Test llvm/utils/unittest/googletest 68 | OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex} 69 | pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT} 70 | ARM contributions llvm/lib/Target/ARM/LICENSE.TXT 71 | md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h 72 | -------------------------------------------------------------------------------- /ljclang/Makefile: -------------------------------------------------------------------------------- 1 | 2 | OS := $(shell uname -s) 3 | MINGW := $(findstring MINGW,$(OS)) 4 | THIS_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) 5 | 6 | 7 | ########## PATHS ########## 8 | 9 | ifeq ($(OS),Linux) 10 | inc := /usr/include 11 | libdir := /usr/lib 12 | lib := -L$(libdir) -lclang 13 | so := .so 14 | else 15 | ifeq ($(MINGW),MINGW) 16 | rdir := /f/g/mod/clang3.3_march2013 17 | inc := $(rdir)/include 18 | lib := $(rdir)/lib/libclang.lib $(rdir)/libclang.dll 19 | so := .dll 20 | else 21 | $(error unknown platform) 22 | endif 23 | endif 24 | 25 | luajit := luajit 26 | asciidoc := asciidoctor 27 | 28 | 29 | ########## OPTIONS ########## 30 | 31 | OPTLEV ?= 2 32 | DEBUG ?= 0 33 | SAN ?= 0 34 | WARN := -std=c99 -pedantic -Wall -Werror-implicit-function-declaration 35 | CFLAGS := 36 | 37 | ifneq ($(SAN),0) 38 | CFLAGS += -fsanitize=address,undefined 39 | endif 40 | 41 | ifneq ($(DEBUG),0) 42 | CFLAGS += -g 43 | endif 44 | 45 | ifeq ($(OS),Linux) 46 | CFLAGS += -I$(inc) -fPIC 47 | else 48 | ifeq ($(MINGW),MINGW) 49 | CFLAGS += -I$(inc) $(lib) 50 | endif 51 | endif 52 | 53 | 54 | ########## RULES ########## 55 | 56 | libljclang_support$(so): ljclang_support.c Makefile 57 | $(CC) $(CFLAGS) $(WARN) -O$(OPTLEV) -shared $< $(lib) -o $@ 58 | 59 | 60 | .PHONY: clean bootstrap doc 61 | 62 | clean: 63 | rm -f libljclang_support$(so) 64 | 65 | CKIND_LUA := ljclang_cursor_kind.lua 66 | EXTRACT_OPTS := -R -p '^CXCursor_' -x '_First' -x '_Last' -x '_GCCAsmStmt' -x '_MacroInstantiation' -s '^CXCursor_' \ 67 | -1 'return { name={' -2 '}, }' -Q 68 | 69 | # Generate list of CXCursorKind names 70 | bootstrap: 71 | @echo 'return {}' > $(CKIND_LUA) 72 | @LD_LIBRARY_PATH=$(THIS_DIR) $(luajit) ./extractdecls.lua $(EXTRACT_OPTS) $(inc)/clang-c/Index.h > $(CKIND_LUA).tmp 73 | @mv $(CKIND_LUA).tmp $(CKIND_LUA) 74 | @printf "\033[1mGenerated $(CKIND_LUA)\033[0m\n" 75 | 76 | doc: 77 | $(asciidoc) README.adoc 78 | -------------------------------------------------------------------------------- /ljclang/README.adoc: -------------------------------------------------------------------------------- 1 | 2 | LJClang -- A LuaJIT-based interface to libclang 3 | =============================================== 4 | Philipp Kutin 5 | :max-width: 56em 6 | 7 | Introduction 8 | ------------ 9 | 10 | :LuaJIT: http://luajit.org/ 11 | :libclang: http://clang.llvm.org/doxygen/group__CINDEX.html 12 | :luaclang-parser: https://github.com/mkottman/luaclang-parser 13 | 14 | LJClang is an interface to {libclang}[libclang] for {LuaJIT}[LuaJIT], modeled 15 | after and mostly API-compatible with {luaclang-parser}[luaclang-parser] by 16 | Michal Kottman. 17 | 18 | Requirements 19 | ------------ 20 | 21 | :LJDownload: http://luajit.org/download.html 22 | 23 | * {LJDownload}[LuaJIT 2.0] (latest Git HEAD of the master branch recommended) 24 | * LLVM/Clang -- read the http://clang.llvm.org/get_started.html[getting 25 | started] guide to find out how to obtain Clang from source. `libclang` is 26 | built and installed along with the Clang compiler. 27 | 28 | Building and usage 29 | ------------------ 30 | 31 | :Clang-Win32: http://www.ishani.org/web/articles/code/clang-win32/ 32 | 33 | Most of LJClang is written in Lua (extensively using LuaJIT's FFI), but due 34 | to currently existing limitations, a support C library has to be built. 35 | 36 | In the provided `Makefile`, adjust the libclang include path, and issue `make` 37 | to build `libljclang_support.so`. 38 | 39 | NOTE: LJClang has been tested on Ubuntu Linux and Windows (using 40 | {Clang-Win32}[Clang-Win32]), but only minor modifications to the build process 41 | should be necessary to get it working with other OSes or configurations. 42 | 43 | From here on, LJClang can be used with LuaJIT by issuing a `require` for 44 | `"ljclang"`. One likely wants to use LJClang from its development directory 45 | without installing it to a system-wide path. Because it expects to find 46 | `libljclang_support.so` and several supporting Lua files, one approach is to 47 | wrap client programs into scripts starting LuaJIT with an environment 48 | containing appropriate `LD_LIBRARY_PATH` and `LUA_PATH` entries. For example, 49 | given the following function in `.bashrc`, 50 | 51 | ---------- 52 | # "LuaJIT with added path of the script directory" 53 | ljwp () 54 | { 55 | local scriptdir=$(cd `dirname $1`; pwd) 56 | LUA_PATH=";;$scriptdir/?.lua" LD_LIBRARY_PATH="$scriptdir" luajit "$@" 57 | } 58 | ---------- 59 | 60 | and assuming that LJClang resides in `~/dl/ljclang`, the `extractdecls.lua` 61 | program described below could be run from anywhere like this: 62 | 63 | ---------- 64 | $~/some/other/dir: ljwp ~/dl/ljclang/extractdecls.lua [args...] 65 | ---------- 66 | 67 | Overview 68 | -------- 69 | 70 | LJClang provides a cursor-based, callback-driven API to the abstract syntax 71 | tree (AST) of C/C++ source files. These are the main classes: 72 | 73 | * `Index` -- represents a set of translation units that could be linked together 74 | * `TranslationUnit` -- a source file together with everything included by it 75 | either directly or transitively 76 | * `Cursor` -- an element in the AST in a translation unit such as a `typedef` 77 | declaration or a statement 78 | * `Type` -- the type of an element (for example, that of a variable, structure 79 | member, or a function's input argument or return value) 80 | 81 | To make something interesting happen, you usually create a single `Index` 82 | object, parse into it one or many translation units, and define a callback 83 | function to be invoked on each visit of a `Cursor` by libclang. 84 | 85 | Example program 86 | --------------- 87 | 88 | :CXCursorKind: http://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013 89 | 90 | The `extractdecls.lua` script accompanied by LJClang can be used to extract 91 | various kinds of C declarations from (usually) headers and print them in 92 | various forms usable as FFI C declarations or descriptive tables with LuaJIT. 93 | 94 | ---------- 95 | Usage: ./extractdecls.lua [our_options...] [clang_options...] 96 | -p 97 | -x [-x ] ... 98 | -s 99 | -1 100 | -2 101 | -C: print lines like 102 | static const int membname = 123; (enums/macros only) 103 | -R: reverse mapping, only if one-to-one. Print lines like 104 | [123] = "membname"; (enums/macros only) 105 | -f : user-provided body for formatting function (enums/macros only) 106 | Accepts args `k', `v'; `f' is string.format. Must return a formatted line. 107 | Example: "return f('%s = %s%s,', k, k:find('KEY_') and '65536+' or '', v)" 108 | Incompatible with -C or -R. 109 | -Q: be quiet 110 | -w: extract what? Can be 111 | EnumConstantDecl (default), TypedefDecl, FunctionDecl, MacroDefinition 112 | ---------- 113 | 114 | In fact, the file `ljclang_cursor_kind.lua` is generated by this program and is 115 | used by LJClang to map values of the enumeration {CXCursorKind}[`enum 116 | CXCursorKind`] to their names. The `bootstrap` target in the `Makefile` 117 | extracts the relevant information using these options: 118 | 119 | ---------- 120 | -R -p '^CXCursor_' -x '_First' -x '_Last' -x '_GCCAsmStmt' -x '_MacroInstantiation' -s '^CXCursor_' \ 121 | -1 'return { name={' -2 '}, }' -Q 122 | ---------- 123 | 124 | Thus, the `typedef` declarations are filtered to begin with ``++CXCursor_++'' 125 | and all ``secondary'' names aliasing the one considered the main one are 126 | rejected. (For example, `CXCursor_AsmStmt` and `CXCursor_GCCAsmStmt` have the 127 | same value.) Finally, the prefix is stripped (`-s`) to yield lines like 128 | 129 | ---------- 130 | [215] = "AsmStmt"; 131 | ---------- 132 | 133 | Reference 134 | --------- 135 | 136 | :clang_createIndex: http://clang.llvm.org/doxygen/group__CINDEX.html#func-members 137 | :CXChildVisitResult: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__TRAVERSAL.html#ga99a9058656e696b622fbefaf5207d715 138 | :clang_parseTranslationUnit: http://clang.llvm.org/doxygen/group__CINDEX__TRANSLATION__UNIT.html#ga2baf83f8c3299788234c8bce55e4472e 139 | :clang_createTranslationUnit: http://clang.llvm.org/doxygen/group__CINDEX__TRANSLATION__UNIT.html#gaa2e74f6e28c438692fd4f5e3d3abda97 140 | 141 | The module returned by `require("ljclang")` contains the following: 142 | 143 | `createIndex([excludePch : boolean [, showDiagnostics : boolean]])` -> `Index`:: 144 | 145 | Binding for {clang_createIndex}[clang_createIndex]. Will create an `Index` into 146 | which you can parse ++TranslationUnit++s. Both input arguments are optional and 147 | default to *false*. 148 | + 149 | NOTE: Loading pre-compiled translation units in not implemented. 150 | 151 | [[ChildVisitResult]] 152 | `ChildVisitResult`:: 153 | 154 | An object containing a mapping of names to values permissible as values 155 | {CXChildVisitResult}[returned] from cursor visitor callbacks: `Break`, 156 | `Continue`, `Recurse`. 157 | 158 | [[regCursorVisitor]] 159 | `regCursorVisitor(visitorfunc)` -> `vf_handle`:: 160 | 161 | Registers a child visitor callback function `visitorfunc` with LJClang, 162 | returning a handle which can be passed to `Cursor:children()`. The callback 163 | function receives two input arguments, `(cursor, parent)` -- with the cursors 164 | of the currently visited entity as well as its parent, and must return a value 165 | from the `ChildVisitResult` enumeration to indicate whether or how libclang 166 | should carry on AST visiting. 167 | + 168 | 169 | NOTE: The `cursor` passed to the visitor callback is only valid during one 170 | particular callback invocation. If it is to be used after the function has 171 | returned, it *must* be copied using the `Cursor` constructor mentioned below. 172 | 173 | `Cursor([cur : Cursor])` -> `Cursor`:: 174 | 175 | A constructor to create a permanent cursor from that received by the visitor 176 | callback. 177 | 178 | 179 | `Index` 180 | ------- 181 | 182 | :TUFlags: http://clang.llvm.org/doxygen/group__CINDEX__TRANSLATION__UNIT.html#enum-members 183 | 184 | `Index:parse([sourceFile : string,] args : table [, opts : table])` -> `TranslationUnit`:: 185 | 186 | Binding for {clang_parseTranslationUnit}[clang_parseTranslationUnit]. This will 187 | parse a given source file `sourceFile` with the command line arguments `args`, 188 | which would be given to the compiler for compilation, containing e.g. include 189 | paths or defines. If `sourceFile` is omitted (i.e. the arguments shifted left 190 | by one), the source file is expected to be named in `args`. 191 | + 192 | The last optional argument `opts` is expected to be a sequence containing 193 | {TUFlags}[`CXTranslationUnit_*`] enum names without the `"CXTranslationUnit_"` 194 | prefix, for example `{ "DetailedPreprocessingRecord" }`. 195 | + 196 | NOTE: Both `args` and `opts` (if given) must not contain an element at index 0. 197 | 198 | ////////// 199 | `Index:load(astFile : string)` -> `TranslationUnit`:: 200 | 201 | Binding for 202 | {clang_createTranslationUnit}[clang_createTranslationUnit]. This will load 203 | the translation unit from an AST file which was constructed using `clang 204 | -emit-ast`. Useful when repeatedly processing large sets of files (like 205 | frameworks). 206 | ////////// 207 | 208 | `TranslationUnit` 209 | ----------------- 210 | 211 | :clang_getTranslationUnitCursor: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__MANIP.html#gaec6e69127920785e74e4a517423f4391 212 | :clang_getFile: http://clang.llvm.org/doxygen/group__CINDEX__FILES.html#gaa0554e2ea48ecd217a29314d3cbd2085 213 | :clang_getDiagnostic: http://clang.llvm.org/doxygen/group__CINDEX__DIAG.html#ga3f54a79e820c2ac9388611e98029afe5 214 | :code_completion_API: http://clang.llvm.org/doxygen/group__CINDEX__CODE__COMPLET.html 215 | :clang_visitChildren: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__TRAVERSAL.html#ga5d0a813d937e1a7dcc35f206ad1f7a91 216 | 217 | `TranslationUnit:cursor()` -> `Cursor`:: 218 | 219 | Binding for 220 | {clang_getTranslationUnitCursor}[clang_getTranslationUnitCursor]. Returns 221 | the `Cursor` representing a given translation unit, which provides access 222 | to information about e.g. functions and types defined in a given file. 223 | 224 | ////////// 225 | `TranslationUnit:file(fileName : string)` -> `string, number`:: 226 | ////////// 227 | `TranslationUnit:file(fileName : string)` -> `string`:: 228 | 229 | Binding for {clang_getFile}[clang_getFile]. Returns the absolute file path 230 | of `fileName`. 231 | + 232 | NOTE: The last modification date is currently not returned as in 233 | luaclang-parser. 234 | ////////// 235 | and a `time_t` last modification time 236 | ////////// 237 | 238 | `TranslationUnit:diagnostics()` -> `{ Diagnostic* }`:: 239 | 240 | Binding for {clang_getDiagnostic}[clang_getDiagnostic]. Returns a table 241 | array of `Diagnostic`, which represent warnings and errors. Each diagnostic 242 | is a table indexable by these keys: `text` -- the diagnostic message, and 243 | `category` -- a diagnostic category (also a string). 244 | 245 | ////////// 246 | `TranslationUnit:codeCompleteAt(file : string, line : number, column : number)` -> `{ Completion* }, { Diagnostics* }`:: 247 | 248 | Binding for {code_completion_API}[code completion API]. Returns the 249 | available code completion options at a given location using prior 250 | content. Each `Completion` is a table consisting of several chunks, each of 251 | which has a text and a {chunk kind}[chunk kind] without the 252 | `CXCompletionChunk_` prefix. If there are any annotations, the 253 | `annotations` key is a table of strings: 254 | 255 | completion = { 256 | priority = number, priority of given completion 257 | chunks = { 258 | kind = string, chunk kind 259 | text = string, chunk text 260 | }, 261 | [annotations = { string* }] 262 | } 263 | ////////// 264 | 265 | `Cursor` 266 | -------- 267 | 268 | :clang_getCursorSemanticParent: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__MANIP.html#gabc327b200d46781cf30cb84d4af3c877 269 | :clang_getCursorLexicalParent: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__MANIP.html#gace7a423874d72b3fdc71d6b0f31830dd 270 | :clang_getCursorSpelling: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gaad1c9b2a1c5ef96cebdbc62f1671c763 271 | :clang_getCursorDisplayName: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gac3eba3224d109a956f9ef96fd4fe5c83 272 | :cursor_kind: http://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013 273 | :clang_Cursor_getArgument: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga673c5529d33eedd0b78aca5ac6fc1d7c 274 | :clang_getCursorResultType: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga6995a2d6352e7136868574b299005a63 275 | :clang_getCursorExtent: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__SOURCE.html#ga79f6544534ab73c78a8494c4c0bc2840 276 | :clang_getCursorReferenced: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gabf059155921552e19fc2abed5b4ff73a 277 | :clang_getCursorDefinition: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#gafcfbec461e561bf13f1e8540bbbd655b 278 | 279 | :clang_getEnumConstantDeclValue: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga6b8585818420e7512feb4c9d209b4f4d 280 | :clang_getEnumConstantUnsignedDeclValue: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaf7cbd4f2d371dd93e8bc997c951a1aef 281 | :clang_getTypedefDeclUnderlyingType: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga8de899fc18dc859b6fe3b97309f4fd52 282 | 283 | :clang_Cursor_getTranslationUnit: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__MANIP.html#ga529f1504710a41ce358d4e8c3161848d 284 | :clang_isCursorDefinition: http://clang.llvm.org/doxygen/group__CINDEX__CURSOR__XREF.html#ga6ad05634a73e693217088eaa693f0010 285 | 286 | You can compare whether two ++Cursor++s represent the same element using the 287 | standard `==` Lua operator. Comparisons with any other type yield *false*. 288 | 289 | `Cursor:children()` -> `{ Cursor* }`:: 290 | `Cursor:children(vf_handle)` -> `boolean`:: 291 | 292 | Binding over {clang_visitChildren}[clang_visitChildren]. This is the main 293 | function for AST traversal. The first form collects the direct descendants of 294 | the given cursor in a table, returning an empty one if none are found. The 295 | second, preferred form accepts a handle of a visitor function previously 296 | registered with <> instead. Here, the 297 | returned value indicates whether the traversal was aborted prematurely due to 298 | the callback returning +<>.Break+. 299 | + 300 | NOTE: Currently, the recommended procedure is to encapsulate the logic of one 301 | particular ``analysis'' into one visitor callback, which may run different 302 | portions of code e.g. conditional on the cursor's kind. (Instead of calling 303 | `Cursor:children(visitor_function_handle)` with a different visitor function 304 | while another invocation of it is active.) 305 | 306 | ////////// 307 | Traverses the direct descendants of a given 308 | cursor and collects them in a table. If no child cursors are found, returns 309 | an empty table. 310 | ////////// 311 | 312 | `Cursor:parent()` -> `Cursor`:: 313 | 314 | Binding for 315 | {clang_getCursorSemanticParent}[clang_getCursorSemanticParent]. Returns a 316 | cursor to the semantic parent of a given element. For example, for a method 317 | cursor, returns its class. For a global declaration, returns the 318 | translation unit cursor. 319 | 320 | `Cursor:lexicalParent()` -> `Cursor`:: 321 | 322 | Binding for 323 | {clang_getCursorLexicalParent}[clang_getCursorLexicalParent]. Returns a 324 | cursor to the lexical parent of a given element. 325 | 326 | `Cursor:name()` -> `string`:: 327 | 328 | Binding over {clang_getCursorSpelling}[clang_getCursorSpelling]. Returns 329 | the name of the entity referenced by cursor. `Cursor` also has `__tostring` 330 | set to this method. 331 | 332 | `Cursor:displayName()` -> `string`:: 333 | 334 | Binding over 335 | {clang_getCursorDisplayName}[clang_getCursorDisplayName]. Returns the 336 | display name of the entity, which for example is a function signature. 337 | 338 | `Cursor:kind()` -> `string`:: 339 | 340 | Returns the {cursor_kind}[cursor kind] without the `CXCursor_` prefix, 341 | e.g. `"FunctionDecl"`. 342 | 343 | `Cursor:haskind(kind : string)` -> `boolean`:: 344 | 345 | Checks whether the cursor has kind given by `kind`, which must be a string 346 | of {CXCursorKind}[`enum CXCursorKind`] names without the `CXCursor_` 347 | prefix. For instance, `if (cur:haskind("TypedefDecl")) then --[[ do 348 | something ]] end` . 349 | 350 | ////////// 351 | kindnum 352 | ////////// 353 | 354 | `Cursor:arguments()` -> `{ Cursor* }`:: 355 | 356 | Binding of {clang_Cursor_getArgument}[clang_Cursor_getArgument]. Returns a 357 | table array of ++Cursor++s representing arguments of a function or a 358 | method. Returns an empty table if a cursor is not a method or function. 359 | 360 | `Cursor:translationUnit()` -> `TranslationUnit`:: 361 | 362 | Binding for 363 | {clang_Cursor_getTranslationUnit}[clang_Cursor_getTranslationUnit]. Returns 364 | the translation unit that a cursor originated from. 365 | 366 | `Cursor:resultType()` -> `Type`:: 367 | 368 | Binding for {clang_getCursorResultType}[clang_getCursorResultType]. For a 369 | function or a method cursor, returns the return type of the function. 370 | 371 | `Cursor:typedefType()` -> `Type`:: 372 | 373 | If the cursor references a typedef declaration, returns its 374 | {clang_getTypedefDeclUnderlyingType}[underlying type]. 375 | 376 | ////////// 377 | XXX: Make error instead? 378 | Otherwise, returns *nil*. 379 | ////////// 380 | 381 | `Cursor:type()` -> `Type`:: 382 | 383 | Returns the `Type` of a given element or *nil* if not available. 384 | 385 | `Cursor:location([linesfirst : boolean])` -> `string, number, number, number, number`:: 386 | 387 | Binding for {clang_getCursorExtent}[clang_getCursorExtent]. Returns the 388 | file name, starting line, starting column, ending line and ending column of 389 | the given cursor. If the optional argument `linesfirst` is true, the numbers 390 | are ordered like starting line, ending line, starting column and ending 391 | column instead. 392 | 393 | ////////// 394 | XXX: Better provide an API around CXSourceRange. 395 | This can be used to look up the text a cursor consists of. 396 | ////////// 397 | 398 | `Cursor:definition()` -> `Cursor`:: 399 | 400 | Binding for {clang_getCursorDefinition}[clang_getCursorDefinition]. For a 401 | reference or declaration, returns a cursor to the definition of the entity, 402 | otherwise returns *nil*. 403 | 404 | `Cursor:referenced()` -> `Cursor`:: 405 | 406 | Binding for {clang_getCursorReferenced}[clang_getCursorReferenced]. For a 407 | reference type, returns a cursor to the element it references, otherwise 408 | returns *nil*. 409 | 410 | `Cursor:access()` -> `string`:: 411 | 412 | When cursor kind is `"AccessSpecifier"`, returns one of `"private"`, 413 | `"protected"` and `"public"`. 414 | 415 | `Cursor:isDefinition()` -> `boolean`:: 416 | 417 | Binding for {clang_isCursorDefinition}[clang_isCursorDefinition]. Determine 418 | whether the declaration pointed to by this cursor is also a definition of 419 | that entity. 420 | 421 | `Cursor:isVirtual()` -> `boolean`:: 422 | 423 | For a C++ method, returns whether the method is virtual. 424 | 425 | `Cursor:isStatic()` -> `boolean`:: 426 | 427 | For a C++ method, returns whether the method is static. 428 | 429 | `Cursor:enumValue([unsigned : boolean])` -> `enum cdata`:: 430 | 431 | If the cursor represents an enumeration constant (`CXCursor_EnumConstantDecl`), 432 | returns its numeric value as a {clang_getEnumConstantDeclValue}[signed] 64-bit 433 | signed integer, or a 64-bit {clang_getEnumConstantUnsignedDeclValue}[unsigned] 434 | integer if `unsigned` is true. 435 | + 436 | NOTE: In C99, an enumeration constant must be in the range of values 437 | representable by an `int` (6.7.2.2#2). LJClang does not check for this 438 | constraint. 439 | 440 | `Cursor:enumval([unsigned : boolean])` -> `number`:: 441 | 442 | Returns the cdata obtained from `enumValue()` as a Lua number, converted 443 | using `tonumber()`. Again, no checking of any kind is carried out. 444 | 445 | `Type` 446 | ------ 447 | 448 | :clang_getTypeKindSpelling: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga6bd7b366d998fc67f4178236398d0666 449 | :clang_getCanonicalType: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaa9815d77adc6823c58be0a0e32010f8c 450 | :clang_getPointeeType: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaafa3eb34932d8da1358d50ed949ff3ee 451 | :clang_isPODType: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga3e7fdbe3d246ed03298bd074c5b3703e 452 | :clang_isConstQualifiedType: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga8c3f8029254d5862bcd595d6c8778e5b 453 | :clang_getTypeDeclaration: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#ga0aad74ea93a2f5dea58fd6fc0db8aad4 454 | :CXTypeKind: http://clang.llvm.org/doxygen/group__CINDEX__TYPES.html#gaad39de597b13a18882c21860f92b095a 455 | 456 | You can compare whether two ++Type++s represent the same type using the standard 457 | `==` Lua operator. Comparisons with any other type yield *false*. 458 | 459 | `Type:name()` -> `string`:: 460 | 461 | Binding of {clang_getTypeKindSpelling}[clang_getTypeKindSpelling]. Returns 462 | one of {CXTypeKind}[`CXTypeKind`] as a string without the `CXType_` 463 | prefix. `Type` also has `__tostring` set to this method. 464 | 465 | `Type:canonical()` -> `Type`:: 466 | 467 | Binding of {clang_getCanonicalType}[clang_getCanonicalType]. Returns 468 | underlying type with all typedefs removed. 469 | + 470 | NOTE: Unlike luaclang-parser, LJClang does *not* dispatch to 471 | `clang_getPointeeType()` for pointer types. 472 | 473 | ////////// 474 | XXX: What was the intention of that? Test out stuff... 475 | ////////// 476 | 477 | `Type:pointee()` -> `Type`:: 478 | 479 | Binding of {clang_getPointeeType}[clang_getPointeeType]. For pointer type 480 | returns the type of the pointee. 481 | 482 | `Type:isPod()` -> `boolean`:: 483 | 484 | Binding of {clang_isPODType}[clang_isPODType]. Returns true if the type is 485 | a ``Plain Old Data'' type. 486 | 487 | `Type:isConst()` -> `boolean`:: 488 | 489 | Binding of 490 | {clang_isConstQualifiedType}[clang_isConstQualifiedType]. Returns true if 491 | the type has a `const` qualifier. 492 | 493 | `Type:declaration()` -> `Cursor`:: 494 | 495 | Binding of {clang_getTypeDeclaration}[clang_getTypeDeclaration]. Returns a 496 | `Cursor` to the declaration of a given type, or *nil*. 497 | 498 | 499 | License 500 | ------- 501 | 502 | Copyright (C) 2013 Philipp Kutin 503 | 504 | (Portions of the documentation copied or adapted from luaclang-parser, Copyright 505 | (C) 2012 Michal Kottman) 506 | 507 | Permission is hereby granted, free of charge, to any person obtaining a copy 508 | of this software and associated documentation files (the "Software"), to deal 509 | in the Software without restriction, including without limitation the rights 510 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 511 | copies of the Software, and to permit persons to whom the Software is 512 | furnished to do so, subject to the following conditions: 513 | 514 | The above copyright notice and this permission notice shall be included in 515 | all copies or substantial portions of the Software. 516 | 517 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 518 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 519 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 520 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 521 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 522 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 523 | THE SOFTWARE. 524 | 525 | -------------------------------------------------------------------------------- /ljclang/createheader.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | 3 | local io = require("io") 4 | local os = require("os") 5 | 6 | local dir = arg[1] 7 | 8 | if (dir == nil) then 9 | print("Usage: ", arg[0], " /usr/path/to/clang-c/ > ljclang_Index_h.lua") 10 | os.exit(1) 11 | end 12 | 13 | local function loadandstrip(filename) 14 | local f, errmsg = io.open(dir.."/"..filename) 15 | if (f==nil) then 16 | print("Error opening file: ", errmsg) 17 | os.exit(2) 18 | end 19 | 20 | local str = f:read("*a") 21 | f:close() 22 | 23 | -- Remove... 24 | return str:gsub("#ifdef __.-#endif\n", "") -- #ifdef __cplusplus/__have_feature ... #endif 25 | :gsub("#define.-[^\\]\n", "") -- multi-line #defines 26 | :gsub("/%*%*.-%*/", "") -- comments, but keep headers with license ref 27 | :gsub("#[^\n]-\n", "") -- single-line preprocessor directives 28 | :gsub("CINDEX_LINKAGE","") 29 | :gsub("CINDEX_DEPRECATED","") 30 | :gsub("time_t", "// time_t") -- clang_getFileTime declaration 31 | end 32 | 33 | local cxstring_h = loadandstrip("CXString.h") 34 | local index_h = loadandstrip("Index.h") 35 | 36 | print("require('ffi').cdef[==========[\n", 37 | cxstring_h, index_h, "]==========]") 38 | -------------------------------------------------------------------------------- /ljclang/extractdecls.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | 3 | local arg = arg 4 | 5 | local string = require("string") 6 | local io = require("io") 7 | local os = require("os") 8 | local table = require("table") 9 | 10 | local cl = require("ljclang") 11 | 12 | local print = print 13 | 14 | ---------- 15 | 16 | local function printf(fmt, ...) 17 | print(string.format(fmt, ...)) 18 | end 19 | 20 | local function usage(hline) 21 | if (hline) then 22 | print(hline) 23 | end 24 | print("Usage: "..arg[0].." [our_options...] [clang_options...]") 25 | print " -p " 26 | print " -x [-x ] ..." 27 | print " -s " 28 | print " -1 " 29 | print " -2 " 30 | print " -C: print lines like" 31 | print " static const int membname = 123; (enums/macros only)" 32 | print " -R: reverse mapping, only if one-to-one. Print lines like" 33 | print " [123] = \"membname\"; (enums/macros only)" 34 | print " -f : user-provided body for formatting function (enums/macros only)" 35 | print " Accepts args `k', `v'; `f' is string.format. Must return a formatted line." 36 | print[[ Example: "return f('%s = %s%s,', k, k:find('KEY_') and '65536+' or '', v)"]] 37 | print " Incompatible with -C or -R." 38 | print " -Q: be quiet" 39 | print " -w: extract what? Can be" 40 | print " EnumConstantDecl (default), TypedefDecl, FunctionDecl, MacroDefinition" 41 | os.exit(1) 42 | end 43 | 44 | local parsecmdline = require("parsecmdline_pk") 45 | 46 | -- Meta-information about options, see parsecmdline_pk. 47 | local opt_meta = { p=true, x=1, s=true, C=false, R=false, Q=false, 48 | ['1']=true, ['2']=true, w=true, f=true } 49 | 50 | local opts, args = parsecmdline.getopts(opt_meta, arg, usage) 51 | 52 | local pat = opts.p 53 | local xpats = opts.x 54 | local spat = opts.s 55 | local constint = opts.C 56 | local reverse = opts.R 57 | local quiet = opts.Q 58 | local what = opts.w or "EnumConstantDecl" 59 | local fmtfuncCode = opts.f 60 | 61 | local extractEnum = (what == "EnumConstantDecl") 62 | local extractMacro = (what:find("^Macro")) 63 | 64 | local printbefore = opts['1'] 65 | local printafter = opts['2'] 66 | 67 | if (#args == 0) then 68 | usage() 69 | end 70 | 71 | if (not (extractEnum or extractMacro) and (constint or reverse)) then 72 | usage("Options -C and -R only available for enum or macro extraction") 73 | end 74 | 75 | local fmtfunc 76 | if (fmtfuncCode) then 77 | if (not (extractEnum or extractMacro)) then 78 | usage("Option -f only available for enum or macro extraction") 79 | end 80 | 81 | if (constint or reverse) then 82 | usage("Option -f is incompatible with -C or -R") 83 | end 84 | 85 | local func, errmsg = loadstring([[ 86 | local f=string.format 87 | return function(k, v) 88 | ]]..fmtfuncCode..[[ 89 | end 90 | ]]) 91 | if (func == nil) then 92 | io.stderr:write("Error loading string: "..errmsg.."\n") 93 | os.exit(1) 94 | end 95 | 96 | fmtfunc = func() 97 | end 98 | 99 | local opts = extractMacro and {"DetailedPreprocessingRecord"} or nil 100 | 101 | local index = cl.createIndex(true, false) 102 | local tu = index:parse(args, opts) 103 | if (tu == nil) then 104 | print('Parsing failed') 105 | os.exit(1) 106 | end 107 | 108 | if (not quiet) then 109 | local diags = tu:diagnostics() 110 | for i=1,#diags do 111 | local d = diags[i] 112 | io.stderr:write(d.text.."\n") 113 | end 114 | end 115 | 116 | -- Mapping of enum value to its name for -R. 117 | local enumname = {} 118 | -- Mapping of running index to enum value for -R. 119 | local enumseq = {} 120 | 121 | local V = cl.ChildVisitResult 122 | 123 | local function checkexclude(name) 124 | for i=1,#xpats do 125 | if (name:find(xpats[i])) then 126 | return true 127 | end 128 | end 129 | end 130 | 131 | -- Get definition string of #define macro definition cursor. 132 | local function getDefStr(cur) 133 | local tokens = cur:_tokens() 134 | -- TOKENIZE_WORKAROUND 135 | -- print("tokens: ["..table.concat(tokens, "|", 2, #tokens).."]") 136 | if (#tokens >= 3) then 137 | tokens[#tokens] = nil 138 | end 139 | return table.concat(tokens, " ", 2, #tokens) 140 | end 141 | 142 | local visitor = cl.regCursorVisitor( 143 | function(cur, parent) 144 | if (extractEnum) then 145 | if (cur:haskind("EnumDecl")) then 146 | return V.Recurse 147 | end 148 | end 149 | 150 | if (cur:haskind(what)) then 151 | local name = cur:displayName() 152 | 153 | if (pat == nil or name:find(pat)) then 154 | local exclude = false 155 | 156 | if (not checkexclude(name)) then 157 | local ourname = spat and name:gsub(spat, "") or name 158 | 159 | if (extractEnum or extractMacro) then 160 | -- Enumeration constants 161 | local val = extractEnum and cur:enumval() or getDefStr(cur) 162 | 163 | -- NOTE: tonumber(val) == nil can only happen with #defines that are not 164 | -- like a literal number. 165 | if (not extractMacro or tonumber(val) ~= nil) then 166 | if (fmtfunc) then 167 | print(fmtfunc(ourname, val)) 168 | elseif (reverse) then 169 | if (enumname[val]) then 170 | printf("Error: enumeration value %d not unique: %s and %s", 171 | val, enumname[val], ourname) 172 | os.exit(2) 173 | end 174 | enumname[val] = ourname 175 | enumseq[#enumseq+1] = val 176 | elseif (constint) then 177 | printf("static const int %s = %s;", ourname, val) 178 | else 179 | printf("%s = %s,", ourname, val) 180 | end 181 | end 182 | elseif (what=="FunctionDecl") then 183 | -- Function declaration 184 | local rettype = cur:resultType() 185 | if (not checkexclude(rettype:name())) then 186 | printf("%s %s;", rettype, ourname) 187 | end 188 | elseif (what=="TypedefDecl") then 189 | -- Type definition 190 | local utype = cur:typedefType() 191 | if (not checkexclude(utype:name())) then 192 | printf("typedef %s %s;", utype, ourname) 193 | end 194 | --[[ 195 | elseif (extractMacro) then 196 | local fn, linebeg, lineend = cur:location(true) 197 | local defstr = getDefStr(cur) 198 | 199 | printf("%s @ %s:%d%s :: %s", ourname, fn, linebeg, 200 | (lineend~=linebeg) and "--"..lineend or "", defstr) 201 | --]] 202 | else 203 | -- Anything else 204 | printf("%s", ourname) 205 | end 206 | end 207 | end 208 | end 209 | 210 | return V.Continue 211 | end) 212 | 213 | if (printbefore) then 214 | print(printbefore) 215 | end 216 | 217 | tu:cursor():children(visitor) 218 | 219 | if (reverse) then 220 | for i=1,#enumseq do 221 | local val = enumseq[i] 222 | local name = enumname[val] 223 | printf("[%d] = %q;", val, name) 224 | end 225 | end 226 | 227 | if (printafter) then 228 | print(printafter) 229 | end 230 | -------------------------------------------------------------------------------- /ljclang/ljclang.lua: -------------------------------------------------------------------------------- 1 | -- LuaJIT-based binding to libclang, modelled after 2 | -- https://github.com/mkottman/luaclang-parser 3 | -- 4 | -- See COPYRIGHT.TXT for the Copyright Notice of LJClang. 5 | -- LICENSE_LLVM.TXT is the license for libclang. 6 | 7 | local assert = assert 8 | local error = error 9 | local print = print 10 | local require = require 11 | local setmetatable = setmetatable 12 | local tonumber = tonumber 13 | local type = type 14 | local unpack = unpack 15 | 16 | local ffi = require("ffi") 17 | 18 | local bit = require("bit") 19 | local io = require("io") 20 | 21 | function lib(basename) 22 | return (ffi.os=="Windows" and "lib" or "")..basename 23 | end 24 | 25 | local clang = ffi.load(lib"clang") 26 | require("ljclang_Index_h") 27 | local support = ffi.load(lib"ljclang_support") 28 | local ckind_name = require("ljclang_cursor_kind").name 29 | 30 | 31 | --==========##########==========-- 32 | 33 | -- The table of externally exposed elements, returned at the end. 34 | local api = {} 35 | 36 | --[[ 37 | local function debugf(fmt, ...) 38 | print(string.format("ljclang: "..fmt, ...)) 39 | end 40 | --[=[]] 41 | local function debugf() end 42 | --]=] 43 | 44 | -- Wrap 'error' in assert-like call to write type checks in one line instead of 45 | -- three. 46 | local function errassert(pred, msg, level) 47 | if (not pred) then 48 | error(msg, level+1) 49 | end 50 | end 51 | 52 | -- CXIndex is a pointer type, wrap it to be able to define a metatable. 53 | local Index_t = ffi.typeof "struct { CXIndex _idx; }" 54 | local TranslationUnit_t_ = ffi.typeof "struct { CXTranslationUnit _tu; }" 55 | -- NOTE: CXCursor is a struct type by itself, but we wrap it to e.g. provide a 56 | -- kind() *method* (CXCursor contains a member of the same name). 57 | local Cursor_t = ffi.typeof "struct { CXCursor _cur; }" 58 | local Type_t = ffi.typeof "struct { CXType _typ; }" 59 | 60 | -- [
] = count 61 | local TUCount = {} 62 | 63 | local function getCXTUaddr(cxtu) 64 | return tostring(cxtu):gsub(".*: 0x", "") 65 | end 66 | 67 | local function TranslationUnit_t(cxtu) 68 | local addr = getCXTUaddr(cxtu) 69 | TUCount[addr] = (TUCount[addr] or 0) + 1 70 | return TranslationUnit_t_(cxtu) 71 | end 72 | 73 | -- Our wrapping type Cursor_t is seen as raw CXCursor on the C side. 74 | assert(ffi.sizeof("CXCursor") == ffi.sizeof(Cursor_t)) 75 | 76 | ffi.cdef([[ 77 | typedef enum CXChildVisitResult (*LJCX_CursorVisitor)( 78 | $ *cursor, $ *parent, CXClientData client_data); 79 | ]], Cursor_t, Cursor_t) 80 | 81 | ffi.cdef[[ 82 | int ljclang_regCursorVisitor(LJCX_CursorVisitor visitor, enum CXCursorKind *kinds, int numkinds); 83 | int ljclang_visitChildren(CXCursor parent, int visitoridx); 84 | ]] 85 | 86 | -- Metatable for our Index_t. 87 | local Index_mt = { 88 | __index = {}, 89 | 90 | __gc = function(self) 91 | -- "The index must not be destroyed until all of the translation units created 92 | -- within that index have been destroyed." 93 | for i=1,#self._tus do 94 | self._tus[i]:_cleanup() 95 | end 96 | clang.clang_disposeIndex(self._idx) 97 | end, 98 | } 99 | 100 | local function NewIndex(cxidx) 101 | assert(ffi.istype("CXIndex", cxidx)) 102 | -- _tus is a list of the Index_t's TranslationUnit_t objects. 103 | local index = { _idx=cxidx, _tus={} } 104 | return setmetatable(index, Index_mt) 105 | end 106 | 107 | local function check_tu_valid(self) 108 | if (self._tu == nil) then 109 | error("Attempt to access freed TranslationUnit", 3) 110 | end 111 | end 112 | 113 | local CXString = ffi.typeof("CXString") 114 | 115 | -- Convert from a libclang's encapsulated CXString to a plain Lua string and 116 | -- dispose of the CXString afterwards. 117 | local function getString(cxstr) 118 | assert(ffi.istype(CXString, cxstr)) 119 | local cstr = clang.clang_getCString(cxstr) 120 | if (cstr == nil) then 121 | return "???" 122 | end 123 | local str = ffi.string(cstr) 124 | clang.clang_disposeString(cxstr) 125 | return str 126 | end 127 | 128 | -- Construct a Cursor_t from a libclang's CXCursor . If is the 129 | -- NULL cursor, return nil. 130 | local function getCursor(cxcur) 131 | return (clang.clang_Cursor_isNull(cxcur) == 0) and Cursor_t(cxcur) or nil 132 | end 133 | 134 | -- Metatable for our TranslationUnit_t. 135 | local TranslationUnit_mt = { 136 | __index = { 137 | _cleanup = function(self) 138 | if (self._tu ~= nil) then 139 | local addr = getCXTUaddr(self._tu) 140 | TUCount[addr] = TUCount[addr]-1 141 | if (TUCount[addr] == 0) then 142 | clang.clang_disposeTranslationUnit(self._tu) 143 | TUCount[addr] = nil 144 | end 145 | self._tu = nil 146 | end 147 | end, 148 | 149 | cursor = function(self) 150 | check_tu_valid(self) 151 | local cxcur = clang.clang_getTranslationUnitCursor(self._tu) 152 | return getCursor(cxcur) 153 | end, 154 | 155 | file = function(self, filename) 156 | check_tu_valid(self) 157 | if (type(filename) ~= "string") then 158 | error(" must be a string", 2) 159 | end 160 | local cxfile = clang.clang_getFile(self._tu, filename) 161 | return getString(clang.clang_getFileName(cxfile)) -- NYI: modification time 162 | end, 163 | 164 | diagnostics = function(self) 165 | check_tu_valid(self) 166 | 167 | local numdiags = clang.clang_getNumDiagnostics(self._tu) 168 | local tab = {} 169 | 170 | for i=0,numdiags-1 do 171 | local diag = clang.clang_getDiagnostic(self._tu, i) 172 | tab[i+1] = { 173 | category = getString(clang.clang_getDiagnosticCategoryText(diag)), 174 | text = getString(clang.clang_formatDiagnostic( 175 | diag, clang.clang_defaultDiagnosticDisplayOptions())) 176 | } 177 | clang.clang_disposeDiagnostic(diag) 178 | end 179 | 180 | return tab 181 | end, 182 | }, 183 | } 184 | 185 | TranslationUnit_mt.__gc = TranslationUnit_mt.__index._cleanup 186 | 187 | local function getType(cxtyp) 188 | return cxtyp.kind ~= 'CXType_Invalid' and Type_t(cxtyp) or nil 189 | end 190 | 191 | local CXFileAr = ffi.typeof("CXFile [1]") 192 | local TwoUnsignedAr = ffi.typeof("unsigned [2]") 193 | 194 | local function getLineCol(cxsrcrange, cxfile, clang_rangefunc, offset) 195 | local cxsrcloc = clang[clang_rangefunc](cxsrcrange) 196 | local linecol = TwoUnsignedAr() 197 | clang.clang_getSpellingLocation(cxsrcloc, cxfile, linecol, linecol+1, offset) 198 | return linecol 199 | end 200 | 201 | local function getPresumedLineCol(cxsrcrange, clang_rangefunc) 202 | local cxsrcloc = clang[clang_rangefunc](cxsrcrange) 203 | local linecol = TwoUnsignedAr() 204 | local file = CXString() 205 | clang.clang_getPresumedLocation(cxsrcloc, file, linecol, linecol+1) 206 | return linecol, getString(file) 207 | end 208 | 209 | local function getSourceRange(cxcur) 210 | local cxsrcrange = clang.clang_getCursorExtent(cxcur) 211 | return (clang.clang_Range_isNull(cxsrcrange) == 0) and cxsrcrange or nil 212 | end 213 | 214 | -- Metatable for our Cursor_t. 215 | local Cursor_mt = { 216 | __eq = function(cur1, cur2) 217 | if (ffi.istype(Cursor_t, cur1) and ffi.istype(Cursor_t, cur2)) then 218 | return (clang.clang_equalCursors(cur1._cur, cur2._cur) ~= 0) 219 | else 220 | return false 221 | end 222 | end, 223 | 224 | __index = { 225 | parent = function(self) 226 | return getCursor(clang.clang_getCursorSemanticParent(self._cur)) 227 | end, 228 | 229 | name = function(self) 230 | return getString(clang.clang_getCursorSpelling(self._cur)) 231 | end, 232 | 233 | displayName = function(self) 234 | return getString(clang.clang_getCursorDisplayName(self._cur)) 235 | end, 236 | 237 | kind = function(self) 238 | local kindnum = tonumber(self:kindnum()) 239 | local kindstr = ckind_name[kindnum] 240 | return kindstr or "Unknown" 241 | end, 242 | 243 | arguments = function(self) 244 | local tab = {} 245 | local numargs = clang.clang_Cursor_getNumArguments(self._cur) 246 | for i=1,numargs do 247 | tab[i] = getCursor(clang.clang_Cursor_getArgument(self._cur, i-1)) 248 | end 249 | return tab 250 | end, 251 | 252 | location = function(self, linesfirst) 253 | local cxsrcrange = getSourceRange(self._cur) 254 | if (cxsrcrange == nil) then 255 | return nil 256 | end 257 | 258 | local cxfilear = CXFileAr() 259 | local offset = TwoUnsignedAr() 260 | local linecolB = getLineCol(cxsrcrange, cxfilear, "clang_getRangeStart", offset) 261 | local filename = getString(clang.clang_getFileName(cxfilear[0])) 262 | local linecolE = getLineCol(cxsrcrange, cxfilear, "clang_getRangeEnd", offset + 1) 263 | 264 | if linesfirst == 'offset' then 265 | return filename, offset[0], offset[1] 266 | elseif (linesfirst) then 267 | -- LJClang order -- IMO you're usually more interested in the 268 | -- line number 269 | return filename, linecolB[0], linecolE[0], linecolB[1], linecolE[1], offset[0], offset[1] 270 | else 271 | -- luaclang-parser order 272 | return filename, linecolB[0], linecolB[1], linecolE[0], linecolE[1], offset[0], offset[1] 273 | end 274 | end, 275 | 276 | presumedLocation = function(self, linesfirst) 277 | local cxsrcrange = getSourceRange(self._cur) 278 | if (cxsrcrange == nil) then 279 | return nil 280 | end 281 | 282 | local linecolB, filename = getPresumedLineCol(cxsrcrange, "clang_getRangeStart") 283 | local linecolE = getPresumedLineCol(cxsrcrange, "clang_getRangeEnd") 284 | 285 | if (linesfirst) then 286 | return filename, linecolB[0], linecolE[0], linecolB[1], linecolE[1] 287 | else 288 | return filename, linecolB[0], linecolB[1], linecolE[0], linecolE[1] 289 | end 290 | end, 291 | 292 | referenced = function(self) 293 | return getCursor(clang.clang_getCursorReferenced(self._cur)) 294 | end, 295 | 296 | definition = function(self) 297 | return getCursor(clang.clang_getCursorDefinition(self._cur)) 298 | end, 299 | 300 | isVirtual = function(self) 301 | return clang.clang_CXXMethod_isVirtual(self._cur) 302 | end, 303 | 304 | isStatic = function(self) 305 | return clang.clang_CXXMethod_isStatic(self._cur) 306 | end, 307 | 308 | type = function(self) 309 | return getType(clang.clang_getCursorType(self._cur)) 310 | end, 311 | 312 | resultType = function(self) 313 | return getType(clang.clang_getCursorResultType(self._cur)) 314 | end, 315 | 316 | access = function(self) 317 | local spec = clang.clang_getCXXAccessSpecifier(self._cur); 318 | 319 | if (spec == 'CX_CXXPublic') then 320 | return "public" 321 | elseif (spec == 'CX_CXXProtected') then 322 | return "protected" 323 | elseif (spec == 'CX_CXXPrivate') then 324 | return "private" 325 | else 326 | assert(spec == 'CX_CXXInvalidAccessSpecifier') 327 | return nil 328 | end 329 | end, 330 | 331 | --== LJClang-specific ==-- 332 | 333 | translationUnit = function(self) 334 | return TranslationUnit_t(clang.clang_Cursor_getTranslationUnit(self._cur)) 335 | end, 336 | 337 | -- XXX: Should be a TranslationUnit_t method instead. 338 | -- 339 | -- NOTE: *Sometimes* returns one token too much, see 340 | -- http://clang-developers.42468.n3.nabble.com/querying-information-about-preprocessing-directives-in-libclang-td2740612.html 341 | -- Related bug report: 342 | -- http://llvm.org/bugs/show_bug.cgi?id=9069 343 | -- Also, see TOKENIZE_WORKAROUND in extractdecls.lua 344 | _tokens = function(self) 345 | local tu = self:translationUnit() 346 | local cxtu = tu._tu 347 | 348 | local _, b, e = self:location('offset') 349 | local cxsrcrange = getSourceRange(self._cur) 350 | if (cxsrcrange == nil) then 351 | return nil 352 | end 353 | 354 | local ntoksar = ffi.new("unsigned [1]") 355 | local tokensar = ffi.new("CXToken *[1]") 356 | clang.clang_tokenize(cxtu, cxsrcrange, tokensar, ntoksar) 357 | local numtoks = ntoksar[0] 358 | local tokens = tokensar[0] 359 | 360 | local tab = {} 361 | local tabextra = {} 362 | 363 | local kinds = { 364 | [tonumber(ffi.C.CXToken_Punctuation)] = 'Punctuation', 365 | [tonumber(ffi.C.CXToken_Keyword)] = 'Keyword', 366 | [tonumber(ffi.C.CXToken_Identifier)] = 'Identifier', 367 | [tonumber(ffi.C.CXToken_Literal)] = 'Literal', 368 | [tonumber(ffi.C.CXToken_Comment)] = 'Comment', 369 | } 370 | for i=0,numtoks-1 do 371 | local sourcerange = clang.clang_getTokenExtent(cxtu, tokens[i]) 372 | local cxfilear = CXFileAr() 373 | local offset = TwoUnsignedAr() 374 | local linecolB = getLineCol(sourcerange, cxfilear, "clang_getRangeStart", offset) 375 | local filename = getString(clang.clang_getFileName(cxfilear[0])) 376 | local linecolE = getLineCol(sourcerange, cxfilear, "clang_getRangeEnd", offset + 1) 377 | local tb, te = offset[0], offset[1] 378 | 379 | local kind = clang.clang_getTokenKind(tokens[i]) 380 | if (clang.clang_getTokenKind(tokens[i]) ~= 'CXToken_Comment') then 381 | if tb >= b and te <= e then 382 | local extent = getString(clang.clang_getTokenSpelling(cxtu, tokens[i])) 383 | tab[#tab+1] = extent 384 | tabextra[#tabextra+1] = { 385 | extent = extent, 386 | kind = kinds[tonumber(kind)], 387 | b = b, e = e, 388 | tb = tb, te = te 389 | } 390 | end 391 | end 392 | end 393 | 394 | clang.clang_disposeTokens(cxtu, tokens, numtoks) 395 | 396 | return tab, tabextra 397 | end, 398 | 399 | lexicalParent = function(self) 400 | return getCursor(clang.clang_getCursorLexicalParent(self._cur)) 401 | end, 402 | 403 | -- Returns an enumeration constant, which in LuaJIT can be compared 404 | -- against a *string*, too. 405 | -- XXX: Should we split into 'kindenum' (giving the enum) and 'kindnum'? 406 | kindnum = function(self) 407 | return clang.clang_getCursorKind(self._cur) 408 | end, 409 | 410 | haskind = function(self, kind) 411 | if (type(kind) == "string") then 412 | return self:kindnum() == "CXCursor_"..kind 413 | else 414 | return self:kindnum() == kind 415 | end 416 | end, 417 | 418 | enumValue = function(self, unsigned) 419 | if (not self:haskind("EnumConstantDecl")) then 420 | error("cursor must have kind EnumConstantDecl", 2) 421 | end 422 | 423 | if (unsigned) then 424 | return clang.clang_getEnumConstantDeclUnsignedValue(self._cur) 425 | else 426 | return clang.clang_getEnumConstantDeclValue(self._cur) 427 | end 428 | end, 429 | 430 | enumval = function(self, unsigned) 431 | return tonumber(self:enumValue(unsigned)) 432 | end, 433 | 434 | isDefinition = function(self) 435 | return (clang.clang_isCursorDefinition(self._cur) ~= 0) 436 | end, 437 | 438 | --[=[ 439 | --| tab = cur:argtypes([alsoret]) 440 | argtypes = function(self, alsoret) 441 | if (clang.clang_Cursor_getNumArguments(self._cur) == -1) then 442 | return nil 443 | end 444 | 445 | local tab = self:arguments() 446 | 447 | if (alsoret) then 448 | tab[0] = self:resultType() 449 | end 450 | 451 | for i=1,#tab do 452 | tab[i] = tab[i]:type() 453 | end 454 | end, 455 | --]=] 456 | typedefType = function(self) 457 | return getType(clang.clang_getTypedefDeclUnderlyingType(self._cur)) 458 | end, 459 | }, 460 | } 461 | 462 | Cursor_mt.__tostring = Cursor_mt.__index.name 463 | 464 | -- Metatable for our Type_t. 465 | local Type_mt = { 466 | __eq = function(typ1, typ2) 467 | if (ffi.istype(Type_t, typ1) and ffi.istype(Type_t, typ2)) then 468 | return (clang.clang_equalTypes(typ1._typ, typ2._typ) ~= 0) 469 | else 470 | return false 471 | end 472 | end, 473 | 474 | __index = { 475 | name = function(self) 476 | return getString(clang.clang_getTypeSpelling(self._typ)) 477 | end, 478 | 479 | canonical = function(self) 480 | -- NOTE: no dispatching to getPointeeType() for pointer types like 481 | -- luaclang-parser. 482 | return getType(clang.clang_getCanonicalType(self._typ)) 483 | end, 484 | 485 | pointee = function(self) 486 | return getType(clang.clang_getPointeeType(self._typ)) 487 | end, 488 | 489 | isConstQualified = function(self) 490 | return not not clang.clang_isConstQualifiedType(self._typ) 491 | end, 492 | 493 | resultType = function(self) 494 | return getType(clang.clang_getResultType(self._typ)) 495 | end, 496 | 497 | arrayElementType = function(self) 498 | return getType(clang.clang_getArrayElementType(self._typ)) 499 | end, 500 | 501 | arraySize = function(self) 502 | return tonumber(clang.clang_getArraySize(self._typ)) 503 | end, 504 | 505 | isConst = function(self) 506 | return (clang.clang_isConstQualifiedType(self._typ) ~= 0); 507 | end, 508 | 509 | isPod = function(self) 510 | return (clang.clang_isPODType(self._typ) ~= 0); 511 | end, 512 | 513 | declaration = function(self) 514 | return getCursor(clang.clang_getTypeDeclaration(self._typ)) 515 | end, 516 | 517 | --== LJClang-specific ==-- 518 | -- Returns an enumeration constant. 519 | kindnum = function(self) 520 | return self._typ.kind 521 | end, 522 | 523 | haskind = function(self, kind) 524 | if (type(kind) == "string") then 525 | return self:kindnum() == "CXType_"..kind 526 | else 527 | return self:kindnum() == kind 528 | end 529 | end, 530 | --[=[ 531 | ]=] 532 | }, 533 | } 534 | 535 | Type_mt.__tostring = Type_mt.__index.name 536 | 537 | 538 | --| index = clang.createIndex([excludeDeclarationsFromPCH [, displayDiagnostics]]) 539 | function api.createIndex(excludeDeclarationsFromPCH, displayDiagnostics) 540 | local cxidx = clang.clang_createIndex(excludeDeclarationsFromPCH or false, 541 | displayDiagnostics or false) 542 | if (cxidx == nil) then 543 | return nil 544 | end 545 | 546 | return NewIndex(cxidx) 547 | end 548 | 549 | -- Is a sequence of strings? 550 | local function iscellstr(tab) 551 | for i=1,#tab do 552 | if (type(tab[i]) ~= "string") then 553 | return false 554 | end 555 | end 556 | -- We require this because in ARGS_FROM_TAB below, an index 0 would be 557 | -- interpreted as the starting index. 558 | return (tab[0] == nil) 559 | end 560 | 561 | local function check_iftab_iscellstr(tab, name) 562 | if (type(tab)=="table") then 563 | if (not iscellstr(tab)) then 564 | error(name.." must be a string sequence when a table", 3) 565 | end 566 | end 567 | end 568 | 569 | --| tunit = index:parse([srcfile, ] args [, opts]) 570 | --| 571 | --| : string or sequence of strings 572 | --| : number or sequence of strings (CXTranslationUnit_* enum members, 573 | --| without the prefix) 574 | function Index_mt.__index.parse(self, srcfile, args, opts) 575 | if (type(srcfile) ~= "string" or 576 | type(args) ~= "string" and type(args) ~= "table") 577 | then 578 | -- Called us like index:parse(args [, opts]), shift input arguments 579 | opts = args 580 | args = srcfile 581 | srcfile = nil 582 | end 583 | 584 | -- Input argument type checking. 585 | 586 | errassert(srcfile==nil or type(srcfile)=="string", " must be a string", 2) 587 | 588 | errassert(type(args)=="string" or type(args)=="table", " must be a string or table", 2) 589 | check_iftab_iscellstr(args, "") 590 | 591 | if (opts == nil) then 592 | opts = 0 593 | else 594 | errassert(type(opts)=="number" or type(opts)=="table", 2) 595 | check_iftab_iscellstr(args, "") 596 | end 597 | 598 | -- Input argument handling. 599 | 600 | if (type(args)=="string") then 601 | local argstab = {} 602 | -- Split delimited by whitespace. 603 | for str in args:gmatch("[^%s]+") do 604 | argstab[#argstab+1] = str 605 | end 606 | args = argstab 607 | end 608 | 609 | if (type(opts)=="table") then 610 | local optflags = {} 611 | for i=1,#opts do 612 | optflags[i] = clang["CXTranslationUnit_"..opts[i]] -- look up the enum 613 | end 614 | opts = bit.bor(unpack(optflags)) 615 | end 616 | 617 | local argsptrs = ffi.new("const char * [?]", #args, args) -- ARGS_FROM_TAB 618 | 619 | -- Create the CXTranslationUnit. 620 | local tunitptr = clang.clang_parseTranslationUnit( 621 | self._idx, srcfile, argsptrs, #args, nil, 0, opts) 622 | 623 | if (tunitptr == nil) then 624 | return nil 625 | end 626 | 627 | -- Wrap it in a TranslationUnit_t. 628 | local tunit = TranslationUnit_t(tunitptr) 629 | 630 | -- Add this TranslationUnit_t to the list of its Index_t's TUs. 631 | self._tus[#self._tus+1] = tunit 632 | 633 | return tunit 634 | end 635 | 636 | 637 | -- enum CXChildVisitResult constants 638 | api.ChildVisitResult = ffi.new[[struct{ 639 | static const int Break = 0; 640 | static const int Continue = 1; 641 | static const int Recurse = 2; 642 | }]] 643 | 644 | function api.regCursorVisitor(visitorfunc) 645 | errassert(type(visitorfunc)=="function", " must be a Lua function", 2) 646 | 647 | local ret = support.ljclang_regCursorVisitor(visitorfunc, nil, 0) 648 | if (ret < 0) then 649 | error("failed registering visitor function, code "..ret, 2) 650 | end 651 | 652 | return ret 653 | end 654 | 655 | local Cursor_ptr_t = ffi.typeof("$ *", Cursor_t) 656 | 657 | function api.Cursor(cur) 658 | errassert(ffi.istype(Cursor_ptr_t, cur), " must be a cursor as passed to the visitor callback", 2) 659 | return Cursor_t(cur[0]) 660 | end 661 | 662 | -- Support for legacy luaclang-parser API collecting direct descendants of a 663 | -- cursor: this will be the table where they go. 664 | local collectTab 665 | 666 | local function collectDirectChildren(cur) 667 | debugf("collectDirectChildren: %s, child cursor kind: %s", tostring(collectTab), cur:kind()) 668 | collectTab[#collectTab+1] = Cursor_t(cur[0]) 669 | return 1 -- Continue 670 | end 671 | 672 | local cdc_visitoridx = api.regCursorVisitor(collectDirectChildren) 673 | 674 | function Cursor_mt.__index.children(self, visitoridx) 675 | if (visitoridx ~= nil) then 676 | -- LJClang way of visiting 677 | local ret = support.ljclang_visitChildren(self._cur, visitoridx) 678 | return (ret ~= 0) 679 | else 680 | -- luaclang-parser way 681 | if (collectTab ~= nil) then 682 | error("children() must not be called while another invocation is active", 2) 683 | collectTab = nil 684 | end 685 | 686 | collectTab = {} 687 | -- XXX: We'll be blocked if the visitor callback errors. 688 | support.ljclang_visitChildren(self._cur, cdc_visitoridx) 689 | local tab = collectTab 690 | collectTab = nil 691 | return tab 692 | end 693 | end 694 | 695 | 696 | -- Register the metatables for the custom ctypes. 697 | ffi.metatype(Index_t, Index_mt) 698 | ffi.metatype(TranslationUnit_t_, TranslationUnit_mt) 699 | ffi.metatype(Cursor_t, Cursor_mt) 700 | ffi.metatype(Type_t, Type_mt) 701 | 702 | -- Done! 703 | return api 704 | -------------------------------------------------------------------------------- /ljclang/ljclang_Index_h.lua: -------------------------------------------------------------------------------- 1 | require('ffi').cdef[==========[ 2 | /*===-- clang-c/CXString.h - C Index strings --------------------*- C -*-===*\ 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 | |* This header provides the interface to C Index strings. *| 12 | |* *| 13 | \*===----------------------------------------------------------------------===*/ 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | typedef struct { 22 | const void *data; 23 | unsigned private_flags; 24 | } CXString; 25 | 26 | 27 | const char *clang_getCString(CXString string); 28 | 29 | 30 | void clang_disposeString(CXString string); 31 | 32 | 33 | 34 | 35 | /*===-- clang-c/Index.h - Indexing Public C Interface -------------*- C -*-===*\ 36 | |* *| 37 | |* The LLVM Compiler Infrastructure *| 38 | |* *| 39 | |* This file is distributed under the University of Illinois Open Source *| 40 | |* License. See LICENSE.TXT for details. *| 41 | |* *| 42 | |*===----------------------------------------------------------------------===*| 43 | |* *| 44 | |* This header provides a public inferface to a Clang library for extracting *| 45 | |* high-level symbol information from source files without exposing the full *| 46 | |* Clang C++ API. *| 47 | |* *| 48 | \*===----------------------------------------------------------------------===*/ 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | typedef void *CXIndex; 64 | 65 | 66 | typedef struct CXTranslationUnitImpl *CXTranslationUnit; 67 | 68 | 69 | typedef void *CXClientData; 70 | 71 | 72 | struct CXUnsavedFile { 73 | 74 | const char *Filename; 75 | 76 | 77 | const char *Contents; 78 | 79 | 80 | unsigned long Length; 81 | }; 82 | 83 | 84 | enum CXAvailabilityKind { 85 | 86 | CXAvailability_Available, 87 | 88 | CXAvailability_Deprecated, 89 | 90 | CXAvailability_NotAvailable, 91 | 92 | CXAvailability_NotAccessible 93 | }; 94 | 95 | 96 | typedef struct CXVersion { 97 | 98 | int Major; 99 | 100 | int Minor; 101 | 102 | int Subminor; 103 | } CXVersion; 104 | 105 | 106 | CXIndex clang_createIndex(int excludeDeclarationsFromPCH, 107 | int displayDiagnostics); 108 | 109 | 110 | void clang_disposeIndex(CXIndex index); 111 | 112 | typedef enum { 113 | 114 | CXGlobalOpt_None = 0x0, 115 | 116 | 117 | CXGlobalOpt_ThreadBackgroundPriorityForIndexing = 0x1, 118 | 119 | 120 | CXGlobalOpt_ThreadBackgroundPriorityForEditing = 0x2, 121 | 122 | 123 | CXGlobalOpt_ThreadBackgroundPriorityForAll = 124 | CXGlobalOpt_ThreadBackgroundPriorityForIndexing | 125 | CXGlobalOpt_ThreadBackgroundPriorityForEditing 126 | 127 | } CXGlobalOptFlags; 128 | 129 | 130 | void clang_CXIndex_setGlobalOptions(CXIndex, unsigned options); 131 | 132 | 133 | unsigned clang_CXIndex_getGlobalOptions(CXIndex); 134 | 135 | 136 | 137 | 138 | typedef void *CXFile; 139 | 140 | 141 | 142 | CXString clang_getFileName(CXFile SFile); 143 | 144 | 145 | // time_t clang_getFileTime(CXFile SFile); 146 | 147 | 148 | typedef struct { 149 | unsigned long long data[3]; 150 | } CXFileUniqueID; 151 | 152 | 153 | int clang_getFileUniqueID(CXFile file, CXFileUniqueID *outID); 154 | 155 | 156 | unsigned 157 | clang_isFileMultipleIncludeGuarded(CXTranslationUnit tu, CXFile file); 158 | 159 | 160 | CXFile clang_getFile(CXTranslationUnit tu, 161 | const char *file_name); 162 | 163 | 164 | 165 | 166 | 167 | 168 | typedef struct { 169 | const void *ptr_data[2]; 170 | unsigned int_data; 171 | } CXSourceLocation; 172 | 173 | 174 | typedef struct { 175 | const void *ptr_data[2]; 176 | unsigned begin_int_data; 177 | unsigned end_int_data; 178 | } CXSourceRange; 179 | 180 | 181 | CXSourceLocation clang_getNullLocation(void); 182 | 183 | 184 | unsigned clang_equalLocations(CXSourceLocation loc1, 185 | CXSourceLocation loc2); 186 | 187 | 188 | CXSourceLocation clang_getLocation(CXTranslationUnit tu, 189 | CXFile file, 190 | unsigned line, 191 | unsigned column); 192 | 193 | CXSourceLocation clang_getLocationForOffset(CXTranslationUnit tu, 194 | CXFile file, 195 | unsigned offset); 196 | 197 | 198 | int clang_Location_isInSystemHeader(CXSourceLocation location); 199 | 200 | 201 | CXSourceRange clang_getNullRange(void); 202 | 203 | 204 | CXSourceRange clang_getRange(CXSourceLocation begin, 205 | CXSourceLocation end); 206 | 207 | 208 | unsigned clang_equalRanges(CXSourceRange range1, 209 | CXSourceRange range2); 210 | 211 | 212 | int clang_Range_isNull(CXSourceRange range); 213 | 214 | 215 | void clang_getExpansionLocation(CXSourceLocation location, 216 | CXFile *file, 217 | unsigned *line, 218 | unsigned *column, 219 | unsigned *offset); 220 | 221 | 222 | void clang_getPresumedLocation(CXSourceLocation location, 223 | CXString *filename, 224 | unsigned *line, 225 | unsigned *column); 226 | 227 | 228 | void clang_getInstantiationLocation(CXSourceLocation location, 229 | CXFile *file, 230 | unsigned *line, 231 | unsigned *column, 232 | unsigned *offset); 233 | 234 | 235 | void clang_getSpellingLocation(CXSourceLocation location, 236 | CXFile *file, 237 | unsigned *line, 238 | unsigned *column, 239 | unsigned *offset); 240 | 241 | 242 | void clang_getFileLocation(CXSourceLocation location, 243 | CXFile *file, 244 | unsigned *line, 245 | unsigned *column, 246 | unsigned *offset); 247 | 248 | 249 | CXSourceLocation clang_getRangeStart(CXSourceRange range); 250 | 251 | 252 | CXSourceLocation clang_getRangeEnd(CXSourceRange range); 253 | 254 | 255 | 256 | 257 | 258 | 259 | enum CXDiagnosticSeverity { 260 | 261 | CXDiagnostic_Ignored = 0, 262 | 263 | 264 | CXDiagnostic_Note = 1, 265 | 266 | 267 | CXDiagnostic_Warning = 2, 268 | 269 | 270 | CXDiagnostic_Error = 3, 271 | 272 | 273 | CXDiagnostic_Fatal = 4 274 | }; 275 | 276 | 277 | typedef void *CXDiagnostic; 278 | 279 | 280 | typedef void *CXDiagnosticSet; 281 | 282 | 283 | unsigned clang_getNumDiagnosticsInSet(CXDiagnosticSet Diags); 284 | 285 | 286 | CXDiagnostic clang_getDiagnosticInSet(CXDiagnosticSet Diags, 287 | unsigned Index); 288 | 289 | 290 | 291 | enum CXLoadDiag_Error { 292 | 293 | CXLoadDiag_None = 0, 294 | 295 | 296 | CXLoadDiag_Unknown = 1, 297 | 298 | 299 | CXLoadDiag_CannotLoad = 2, 300 | 301 | 302 | CXLoadDiag_InvalidFile = 3 303 | }; 304 | 305 | 306 | CXDiagnosticSet clang_loadDiagnostics(const char *file, 307 | enum CXLoadDiag_Error *error, 308 | CXString *errorString); 309 | 310 | 311 | void clang_disposeDiagnosticSet(CXDiagnosticSet Diags); 312 | 313 | 314 | CXDiagnosticSet clang_getChildDiagnostics(CXDiagnostic D); 315 | 316 | 317 | unsigned clang_getNumDiagnostics(CXTranslationUnit Unit); 318 | 319 | 320 | CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, 321 | unsigned Index); 322 | 323 | 324 | CXDiagnosticSet 325 | clang_getDiagnosticSetFromTU(CXTranslationUnit Unit); 326 | 327 | 328 | void clang_disposeDiagnostic(CXDiagnostic Diagnostic); 329 | 330 | 331 | enum CXDiagnosticDisplayOptions { 332 | 333 | CXDiagnostic_DisplaySourceLocation = 0x01, 334 | 335 | 336 | CXDiagnostic_DisplayColumn = 0x02, 337 | 338 | 339 | CXDiagnostic_DisplaySourceRanges = 0x04, 340 | 341 | 342 | CXDiagnostic_DisplayOption = 0x08, 343 | 344 | 345 | CXDiagnostic_DisplayCategoryId = 0x10, 346 | 347 | 348 | CXDiagnostic_DisplayCategoryName = 0x20 349 | }; 350 | 351 | 352 | CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, 353 | unsigned Options); 354 | 355 | 356 | unsigned clang_defaultDiagnosticDisplayOptions(void); 357 | 358 | 359 | enum CXDiagnosticSeverity 360 | clang_getDiagnosticSeverity(CXDiagnostic); 361 | 362 | 363 | CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic); 364 | 365 | 366 | CXString clang_getDiagnosticSpelling(CXDiagnostic); 367 | 368 | 369 | CXString clang_getDiagnosticOption(CXDiagnostic Diag, 370 | CXString *Disable); 371 | 372 | 373 | unsigned clang_getDiagnosticCategory(CXDiagnostic); 374 | 375 | 376 | 377 | CXString clang_getDiagnosticCategoryName(unsigned Category); 378 | 379 | 380 | CXString clang_getDiagnosticCategoryText(CXDiagnostic); 381 | 382 | 383 | unsigned clang_getDiagnosticNumRanges(CXDiagnostic); 384 | 385 | 386 | CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diagnostic, 387 | unsigned Range); 388 | 389 | 390 | unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diagnostic); 391 | 392 | 393 | CXString clang_getDiagnosticFixIt(CXDiagnostic Diagnostic, 394 | unsigned FixIt, 395 | CXSourceRange *ReplacementRange); 396 | 397 | 398 | 399 | 400 | 401 | 402 | CXString 403 | clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit); 404 | 405 | 406 | CXTranslationUnit clang_createTranslationUnitFromSourceFile( 407 | CXIndex CIdx, 408 | const char *source_filename, 409 | int num_clang_command_line_args, 410 | const char * const *clang_command_line_args, 411 | unsigned num_unsaved_files, 412 | struct CXUnsavedFile *unsaved_files); 413 | 414 | 415 | CXTranslationUnit clang_createTranslationUnit(CXIndex, 416 | const char *ast_filename); 417 | 418 | 419 | enum CXTranslationUnit_Flags { 420 | 421 | CXTranslationUnit_None = 0x0, 422 | 423 | 424 | CXTranslationUnit_DetailedPreprocessingRecord = 0x01, 425 | 426 | 427 | CXTranslationUnit_Incomplete = 0x02, 428 | 429 | 430 | CXTranslationUnit_PrecompiledPreamble = 0x04, 431 | 432 | 433 | CXTranslationUnit_CacheCompletionResults = 0x08, 434 | 435 | 436 | CXTranslationUnit_ForSerialization = 0x10, 437 | 438 | 439 | CXTranslationUnit_CXXChainedPCH = 0x20, 440 | 441 | 442 | CXTranslationUnit_SkipFunctionBodies = 0x40, 443 | 444 | 445 | CXTranslationUnit_IncludeBriefCommentsInCodeCompletion = 0x80 446 | }; 447 | 448 | 449 | unsigned clang_defaultEditingTranslationUnitOptions(void); 450 | 451 | 452 | CXTranslationUnit clang_parseTranslationUnit(CXIndex CIdx, 453 | const char *source_filename, 454 | const char * const *command_line_args, 455 | int num_command_line_args, 456 | struct CXUnsavedFile *unsaved_files, 457 | unsigned num_unsaved_files, 458 | unsigned options); 459 | 460 | 461 | enum CXSaveTranslationUnit_Flags { 462 | 463 | CXSaveTranslationUnit_None = 0x0 464 | }; 465 | 466 | 467 | unsigned clang_defaultSaveOptions(CXTranslationUnit TU); 468 | 469 | 470 | enum CXSaveError { 471 | 472 | CXSaveError_None = 0, 473 | 474 | 475 | CXSaveError_Unknown = 1, 476 | 477 | 478 | CXSaveError_TranslationErrors = 2, 479 | 480 | 481 | CXSaveError_InvalidTU = 3 482 | }; 483 | 484 | 485 | int clang_saveTranslationUnit(CXTranslationUnit TU, 486 | const char *FileName, 487 | unsigned options); 488 | 489 | 490 | void clang_disposeTranslationUnit(CXTranslationUnit); 491 | 492 | 493 | enum CXReparse_Flags { 494 | 495 | CXReparse_None = 0x0 496 | }; 497 | 498 | 499 | unsigned clang_defaultReparseOptions(CXTranslationUnit TU); 500 | 501 | 502 | int clang_reparseTranslationUnit(CXTranslationUnit TU, 503 | unsigned num_unsaved_files, 504 | struct CXUnsavedFile *unsaved_files, 505 | unsigned options); 506 | 507 | 508 | enum CXTUResourceUsageKind { 509 | CXTUResourceUsage_AST = 1, 510 | CXTUResourceUsage_Identifiers = 2, 511 | CXTUResourceUsage_Selectors = 3, 512 | CXTUResourceUsage_GlobalCompletionResults = 4, 513 | CXTUResourceUsage_SourceManagerContentCache = 5, 514 | CXTUResourceUsage_AST_SideTables = 6, 515 | CXTUResourceUsage_SourceManager_Membuffer_Malloc = 7, 516 | CXTUResourceUsage_SourceManager_Membuffer_MMap = 8, 517 | CXTUResourceUsage_ExternalASTSource_Membuffer_Malloc = 9, 518 | CXTUResourceUsage_ExternalASTSource_Membuffer_MMap = 10, 519 | CXTUResourceUsage_Preprocessor = 11, 520 | CXTUResourceUsage_PreprocessingRecord = 12, 521 | CXTUResourceUsage_SourceManager_DataStructures = 13, 522 | CXTUResourceUsage_Preprocessor_HeaderSearch = 14, 523 | CXTUResourceUsage_MEMORY_IN_BYTES_BEGIN = CXTUResourceUsage_AST, 524 | CXTUResourceUsage_MEMORY_IN_BYTES_END = 525 | CXTUResourceUsage_Preprocessor_HeaderSearch, 526 | 527 | CXTUResourceUsage_First = CXTUResourceUsage_AST, 528 | CXTUResourceUsage_Last = CXTUResourceUsage_Preprocessor_HeaderSearch 529 | }; 530 | 531 | 532 | 533 | const char *clang_getTUResourceUsageName(enum CXTUResourceUsageKind kind); 534 | 535 | typedef struct CXTUResourceUsageEntry { 536 | /* \brief The memory usage category. */ 537 | enum CXTUResourceUsageKind kind; 538 | /* \brief Amount of resources used. 539 | The units will depend on the resource kind. */ 540 | unsigned long amount; 541 | } CXTUResourceUsageEntry; 542 | 543 | 544 | typedef struct CXTUResourceUsage { 545 | /* \brief Private data member, used for queries. */ 546 | void *data; 547 | 548 | /* \brief The number of entries in the 'entries' array. */ 549 | unsigned numEntries; 550 | 551 | /* \brief An array of key-value pairs, representing the breakdown of memory 552 | usage. */ 553 | CXTUResourceUsageEntry *entries; 554 | 555 | } CXTUResourceUsage; 556 | 557 | 558 | CXTUResourceUsage clang_getCXTUResourceUsage(CXTranslationUnit TU); 559 | 560 | void clang_disposeCXTUResourceUsage(CXTUResourceUsage usage); 561 | 562 | 563 | 564 | 565 | enum CXCursorKind { 566 | /* Declarations */ 567 | 568 | CXCursor_UnexposedDecl = 1, 569 | 570 | CXCursor_StructDecl = 2, 571 | 572 | CXCursor_UnionDecl = 3, 573 | 574 | CXCursor_ClassDecl = 4, 575 | 576 | CXCursor_EnumDecl = 5, 577 | 578 | CXCursor_FieldDecl = 6, 579 | 580 | CXCursor_EnumConstantDecl = 7, 581 | 582 | CXCursor_FunctionDecl = 8, 583 | 584 | CXCursor_VarDecl = 9, 585 | 586 | CXCursor_ParmDecl = 10, 587 | 588 | CXCursor_ObjCInterfaceDecl = 11, 589 | 590 | CXCursor_ObjCCategoryDecl = 12, 591 | 592 | CXCursor_ObjCProtocolDecl = 13, 593 | 594 | CXCursor_ObjCPropertyDecl = 14, 595 | 596 | CXCursor_ObjCIvarDecl = 15, 597 | 598 | CXCursor_ObjCInstanceMethodDecl = 16, 599 | 600 | CXCursor_ObjCClassMethodDecl = 17, 601 | 602 | CXCursor_ObjCImplementationDecl = 18, 603 | 604 | CXCursor_ObjCCategoryImplDecl = 19, 605 | 606 | CXCursor_TypedefDecl = 20, 607 | 608 | CXCursor_CXXMethod = 21, 609 | 610 | CXCursor_Namespace = 22, 611 | 612 | CXCursor_LinkageSpec = 23, 613 | 614 | CXCursor_Constructor = 24, 615 | 616 | CXCursor_Destructor = 25, 617 | 618 | CXCursor_ConversionFunction = 26, 619 | 620 | CXCursor_TemplateTypeParameter = 27, 621 | 622 | CXCursor_NonTypeTemplateParameter = 28, 623 | 624 | CXCursor_TemplateTemplateParameter = 29, 625 | 626 | CXCursor_FunctionTemplate = 30, 627 | 628 | CXCursor_ClassTemplate = 31, 629 | 630 | CXCursor_ClassTemplatePartialSpecialization = 32, 631 | 632 | CXCursor_NamespaceAlias = 33, 633 | 634 | CXCursor_UsingDirective = 34, 635 | 636 | CXCursor_UsingDeclaration = 35, 637 | 638 | CXCursor_TypeAliasDecl = 36, 639 | 640 | CXCursor_ObjCSynthesizeDecl = 37, 641 | 642 | CXCursor_ObjCDynamicDecl = 38, 643 | 644 | CXCursor_CXXAccessSpecifier = 39, 645 | 646 | CXCursor_FirstDecl = CXCursor_UnexposedDecl, 647 | CXCursor_LastDecl = CXCursor_CXXAccessSpecifier, 648 | 649 | /* References */ 650 | CXCursor_FirstRef = 40, /* Decl references */ 651 | CXCursor_ObjCSuperClassRef = 40, 652 | CXCursor_ObjCProtocolRef = 41, 653 | CXCursor_ObjCClassRef = 42, 654 | 655 | CXCursor_TypeRef = 43, 656 | CXCursor_CXXBaseSpecifier = 44, 657 | 658 | CXCursor_TemplateRef = 45, 659 | 660 | CXCursor_NamespaceRef = 46, 661 | 662 | CXCursor_MemberRef = 47, 663 | 664 | CXCursor_LabelRef = 48, 665 | 666 | 667 | CXCursor_OverloadedDeclRef = 49, 668 | 669 | 670 | CXCursor_VariableRef = 50, 671 | 672 | CXCursor_LastRef = CXCursor_VariableRef, 673 | 674 | /* Error conditions */ 675 | CXCursor_FirstInvalid = 70, 676 | CXCursor_InvalidFile = 70, 677 | CXCursor_NoDeclFound = 71, 678 | CXCursor_NotImplemented = 72, 679 | CXCursor_InvalidCode = 73, 680 | CXCursor_LastInvalid = CXCursor_InvalidCode, 681 | 682 | /* Expressions */ 683 | CXCursor_FirstExpr = 100, 684 | 685 | 686 | CXCursor_UnexposedExpr = 100, 687 | 688 | 689 | CXCursor_DeclRefExpr = 101, 690 | 691 | 692 | CXCursor_MemberRefExpr = 102, 693 | 694 | 695 | CXCursor_CallExpr = 103, 696 | 697 | 698 | CXCursor_ObjCMessageExpr = 104, 699 | 700 | 701 | CXCursor_BlockExpr = 105, 702 | 703 | 704 | CXCursor_IntegerLiteral = 106, 705 | 706 | 707 | CXCursor_FloatingLiteral = 107, 708 | 709 | 710 | CXCursor_ImaginaryLiteral = 108, 711 | 712 | 713 | CXCursor_StringLiteral = 109, 714 | 715 | 716 | CXCursor_CharacterLiteral = 110, 717 | 718 | 719 | CXCursor_ParenExpr = 111, 720 | 721 | 722 | CXCursor_UnaryOperator = 112, 723 | 724 | 725 | CXCursor_ArraySubscriptExpr = 113, 726 | 727 | 728 | CXCursor_BinaryOperator = 114, 729 | 730 | 731 | CXCursor_CompoundAssignOperator = 115, 732 | 733 | 734 | CXCursor_ConditionalOperator = 116, 735 | 736 | 737 | CXCursor_CStyleCastExpr = 117, 738 | 739 | 740 | CXCursor_CompoundLiteralExpr = 118, 741 | 742 | 743 | CXCursor_InitListExpr = 119, 744 | 745 | 746 | CXCursor_AddrLabelExpr = 120, 747 | 748 | 749 | CXCursor_StmtExpr = 121, 750 | 751 | 752 | CXCursor_GenericSelectionExpr = 122, 753 | 754 | 755 | CXCursor_GNUNullExpr = 123, 756 | 757 | 758 | CXCursor_CXXStaticCastExpr = 124, 759 | 760 | 761 | CXCursor_CXXDynamicCastExpr = 125, 762 | 763 | 764 | CXCursor_CXXReinterpretCastExpr = 126, 765 | 766 | 767 | CXCursor_CXXConstCastExpr = 127, 768 | 769 | 770 | CXCursor_CXXFunctionalCastExpr = 128, 771 | 772 | 773 | CXCursor_CXXTypeidExpr = 129, 774 | 775 | 776 | CXCursor_CXXBoolLiteralExpr = 130, 777 | 778 | 779 | CXCursor_CXXNullPtrLiteralExpr = 131, 780 | 781 | 782 | CXCursor_CXXThisExpr = 132, 783 | 784 | 785 | CXCursor_CXXThrowExpr = 133, 786 | 787 | 788 | CXCursor_CXXNewExpr = 134, 789 | 790 | 791 | CXCursor_CXXDeleteExpr = 135, 792 | 793 | 794 | CXCursor_UnaryExpr = 136, 795 | 796 | 797 | CXCursor_ObjCStringLiteral = 137, 798 | 799 | 800 | CXCursor_ObjCEncodeExpr = 138, 801 | 802 | 803 | CXCursor_ObjCSelectorExpr = 139, 804 | 805 | 806 | CXCursor_ObjCProtocolExpr = 140, 807 | 808 | 809 | CXCursor_ObjCBridgedCastExpr = 141, 810 | 811 | 812 | CXCursor_PackExpansionExpr = 142, 813 | 814 | 815 | CXCursor_SizeOfPackExpr = 143, 816 | 817 | /* \brief Represents a C++ lambda expression that produces a local function 818 | * object. 819 | * 820 | * \code 821 | * void abssort(float *x, unsigned N) { 822 | * std::sort(x, x + N, 823 | * [](float a, float b) { 824 | * return std::abs(a) < std::abs(b); 825 | * }); 826 | * } 827 | * \endcode 828 | */ 829 | CXCursor_LambdaExpr = 144, 830 | 831 | 832 | CXCursor_ObjCBoolLiteralExpr = 145, 833 | 834 | 835 | CXCursor_ObjCSelfExpr = 146, 836 | 837 | CXCursor_LastExpr = CXCursor_ObjCSelfExpr, 838 | 839 | /* Statements */ 840 | CXCursor_FirstStmt = 200, 841 | 842 | CXCursor_UnexposedStmt = 200, 843 | 844 | 845 | CXCursor_LabelStmt = 201, 846 | 847 | 848 | CXCursor_CompoundStmt = 202, 849 | 850 | 851 | CXCursor_CaseStmt = 203, 852 | 853 | 854 | CXCursor_DefaultStmt = 204, 855 | 856 | 857 | CXCursor_IfStmt = 205, 858 | 859 | 860 | CXCursor_SwitchStmt = 206, 861 | 862 | 863 | CXCursor_WhileStmt = 207, 864 | 865 | 866 | CXCursor_DoStmt = 208, 867 | 868 | 869 | CXCursor_ForStmt = 209, 870 | 871 | 872 | CXCursor_GotoStmt = 210, 873 | 874 | 875 | CXCursor_IndirectGotoStmt = 211, 876 | 877 | 878 | CXCursor_ContinueStmt = 212, 879 | 880 | 881 | CXCursor_BreakStmt = 213, 882 | 883 | 884 | CXCursor_ReturnStmt = 214, 885 | 886 | 887 | CXCursor_GCCAsmStmt = 215, 888 | CXCursor_AsmStmt = CXCursor_GCCAsmStmt, 889 | 890 | 891 | CXCursor_ObjCAtTryStmt = 216, 892 | 893 | 894 | CXCursor_ObjCAtCatchStmt = 217, 895 | 896 | 897 | CXCursor_ObjCAtFinallyStmt = 218, 898 | 899 | 900 | CXCursor_ObjCAtThrowStmt = 219, 901 | 902 | 903 | CXCursor_ObjCAtSynchronizedStmt = 220, 904 | 905 | 906 | CXCursor_ObjCAutoreleasePoolStmt = 221, 907 | 908 | 909 | CXCursor_ObjCForCollectionStmt = 222, 910 | 911 | 912 | CXCursor_CXXCatchStmt = 223, 913 | 914 | 915 | CXCursor_CXXTryStmt = 224, 916 | 917 | 918 | CXCursor_CXXForRangeStmt = 225, 919 | 920 | 921 | CXCursor_SEHTryStmt = 226, 922 | 923 | 924 | CXCursor_SEHExceptStmt = 227, 925 | 926 | 927 | CXCursor_SEHFinallyStmt = 228, 928 | 929 | 930 | CXCursor_MSAsmStmt = 229, 931 | 932 | 933 | CXCursor_NullStmt = 230, 934 | 935 | 936 | CXCursor_DeclStmt = 231, 937 | 938 | 939 | CXCursor_OMPParallelDirective = 232, 940 | 941 | CXCursor_LastStmt = CXCursor_OMPParallelDirective, 942 | 943 | 944 | CXCursor_TranslationUnit = 300, 945 | 946 | /* Attributes */ 947 | CXCursor_FirstAttr = 400, 948 | 949 | CXCursor_UnexposedAttr = 400, 950 | 951 | CXCursor_IBActionAttr = 401, 952 | CXCursor_IBOutletAttr = 402, 953 | CXCursor_IBOutletCollectionAttr = 403, 954 | CXCursor_CXXFinalAttr = 404, 955 | CXCursor_CXXOverrideAttr = 405, 956 | CXCursor_AnnotateAttr = 406, 957 | CXCursor_AsmLabelAttr = 407, 958 | CXCursor_LastAttr = CXCursor_AsmLabelAttr, 959 | 960 | /* Preprocessing */ 961 | CXCursor_PreprocessingDirective = 500, 962 | CXCursor_MacroDefinition = 501, 963 | CXCursor_MacroExpansion = 502, 964 | CXCursor_MacroInstantiation = CXCursor_MacroExpansion, 965 | CXCursor_InclusionDirective = 503, 966 | CXCursor_FirstPreprocessing = CXCursor_PreprocessingDirective, 967 | CXCursor_LastPreprocessing = CXCursor_InclusionDirective, 968 | 969 | /* Extra Declarations */ 970 | 971 | CXCursor_ModuleImportDecl = 600, 972 | CXCursor_FirstExtraDecl = CXCursor_ModuleImportDecl, 973 | CXCursor_LastExtraDecl = CXCursor_ModuleImportDecl 974 | }; 975 | 976 | 977 | typedef struct { 978 | enum CXCursorKind kind; 979 | int xdata; 980 | const void *data[3]; 981 | } CXCursor; 982 | 983 | 984 | typedef struct { 985 | const void *ASTNode; 986 | CXTranslationUnit TranslationUnit; 987 | } CXComment; 988 | 989 | 990 | 991 | 992 | CXCursor clang_getNullCursor(void); 993 | 994 | 995 | CXCursor clang_getTranslationUnitCursor(CXTranslationUnit); 996 | 997 | 998 | unsigned clang_equalCursors(CXCursor, CXCursor); 999 | 1000 | 1001 | int clang_Cursor_isNull(CXCursor cursor); 1002 | 1003 | 1004 | unsigned clang_hashCursor(CXCursor); 1005 | 1006 | 1007 | enum CXCursorKind clang_getCursorKind(CXCursor); 1008 | 1009 | 1010 | unsigned clang_isDeclaration(enum CXCursorKind); 1011 | 1012 | 1013 | unsigned clang_isReference(enum CXCursorKind); 1014 | 1015 | 1016 | unsigned clang_isExpression(enum CXCursorKind); 1017 | 1018 | 1019 | unsigned clang_isStatement(enum CXCursorKind); 1020 | 1021 | 1022 | unsigned clang_isAttribute(enum CXCursorKind); 1023 | 1024 | 1025 | unsigned clang_isInvalid(enum CXCursorKind); 1026 | 1027 | 1028 | unsigned clang_isTranslationUnit(enum CXCursorKind); 1029 | 1030 | 1031 | unsigned clang_isPreprocessing(enum CXCursorKind); 1032 | 1033 | 1034 | unsigned clang_isUnexposed(enum CXCursorKind); 1035 | 1036 | 1037 | enum CXLinkageKind { 1038 | 1039 | CXLinkage_Invalid, 1040 | 1041 | CXLinkage_NoLinkage, 1042 | 1043 | CXLinkage_Internal, 1044 | 1045 | CXLinkage_UniqueExternal, 1046 | 1047 | CXLinkage_External 1048 | }; 1049 | 1050 | 1051 | enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor); 1052 | 1053 | 1054 | enum CXAvailabilityKind 1055 | clang_getCursorAvailability(CXCursor cursor); 1056 | 1057 | 1058 | typedef struct CXPlatformAvailability { 1059 | 1060 | CXString Platform; 1061 | 1062 | CXVersion Introduced; 1063 | 1064 | CXVersion Deprecated; 1065 | 1066 | CXVersion Obsoleted; 1067 | 1068 | int Unavailable; 1069 | 1070 | CXString Message; 1071 | } CXPlatformAvailability; 1072 | 1073 | 1074 | int 1075 | clang_getCursorPlatformAvailability(CXCursor cursor, 1076 | int *always_deprecated, 1077 | CXString *deprecated_message, 1078 | int *always_unavailable, 1079 | CXString *unavailable_message, 1080 | CXPlatformAvailability *availability, 1081 | int availability_size); 1082 | 1083 | 1084 | void 1085 | clang_disposeCXPlatformAvailability(CXPlatformAvailability *availability); 1086 | 1087 | 1088 | enum CXLanguageKind { 1089 | CXLanguage_Invalid = 0, 1090 | CXLanguage_C, 1091 | CXLanguage_ObjC, 1092 | CXLanguage_CPlusPlus 1093 | }; 1094 | 1095 | 1096 | enum CXLanguageKind clang_getCursorLanguage(CXCursor cursor); 1097 | 1098 | 1099 | CXTranslationUnit clang_Cursor_getTranslationUnit(CXCursor); 1100 | 1101 | 1102 | 1103 | typedef struct CXCursorSetImpl *CXCursorSet; 1104 | 1105 | 1106 | CXCursorSet clang_createCXCursorSet(void); 1107 | 1108 | 1109 | void clang_disposeCXCursorSet(CXCursorSet cset); 1110 | 1111 | 1112 | unsigned clang_CXCursorSet_contains(CXCursorSet cset, 1113 | CXCursor cursor); 1114 | 1115 | 1116 | unsigned clang_CXCursorSet_insert(CXCursorSet cset, 1117 | CXCursor cursor); 1118 | 1119 | 1120 | CXCursor clang_getCursorSemanticParent(CXCursor cursor); 1121 | 1122 | 1123 | CXCursor clang_getCursorLexicalParent(CXCursor cursor); 1124 | 1125 | 1126 | void clang_getOverriddenCursors(CXCursor cursor, 1127 | CXCursor **overridden, 1128 | unsigned *num_overridden); 1129 | 1130 | 1131 | void clang_disposeOverriddenCursors(CXCursor *overridden); 1132 | 1133 | 1134 | CXFile clang_getIncludedFile(CXCursor cursor); 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | CXCursor clang_getCursor(CXTranslationUnit, CXSourceLocation); 1142 | 1143 | 1144 | CXSourceLocation clang_getCursorLocation(CXCursor); 1145 | 1146 | 1147 | CXSourceRange clang_getCursorExtent(CXCursor); 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | enum CXTypeKind { 1155 | 1156 | CXType_Invalid = 0, 1157 | 1158 | 1159 | CXType_Unexposed = 1, 1160 | 1161 | /* Builtin types */ 1162 | CXType_Void = 2, 1163 | CXType_Bool = 3, 1164 | CXType_Char_U = 4, 1165 | CXType_UChar = 5, 1166 | CXType_Char16 = 6, 1167 | CXType_Char32 = 7, 1168 | CXType_UShort = 8, 1169 | CXType_UInt = 9, 1170 | CXType_ULong = 10, 1171 | CXType_ULongLong = 11, 1172 | CXType_UInt128 = 12, 1173 | CXType_Char_S = 13, 1174 | CXType_SChar = 14, 1175 | CXType_WChar = 15, 1176 | CXType_Short = 16, 1177 | CXType_Int = 17, 1178 | CXType_Long = 18, 1179 | CXType_LongLong = 19, 1180 | CXType_Int128 = 20, 1181 | CXType_Float = 21, 1182 | CXType_Double = 22, 1183 | CXType_LongDouble = 23, 1184 | CXType_NullPtr = 24, 1185 | CXType_Overload = 25, 1186 | CXType_Dependent = 26, 1187 | CXType_ObjCId = 27, 1188 | CXType_ObjCClass = 28, 1189 | CXType_ObjCSel = 29, 1190 | CXType_FirstBuiltin = CXType_Void, 1191 | CXType_LastBuiltin = CXType_ObjCSel, 1192 | 1193 | CXType_Complex = 100, 1194 | CXType_Pointer = 101, 1195 | CXType_BlockPointer = 102, 1196 | CXType_LValueReference = 103, 1197 | CXType_RValueReference = 104, 1198 | CXType_Record = 105, 1199 | CXType_Enum = 106, 1200 | CXType_Typedef = 107, 1201 | CXType_ObjCInterface = 108, 1202 | CXType_ObjCObjectPointer = 109, 1203 | CXType_FunctionNoProto = 110, 1204 | CXType_FunctionProto = 111, 1205 | CXType_ConstantArray = 112, 1206 | CXType_Vector = 113, 1207 | CXType_IncompleteArray = 114, 1208 | CXType_VariableArray = 115, 1209 | CXType_DependentSizedArray = 116 1210 | }; 1211 | 1212 | 1213 | enum CXCallingConv { 1214 | CXCallingConv_Default = 0, 1215 | CXCallingConv_C = 1, 1216 | CXCallingConv_X86StdCall = 2, 1217 | CXCallingConv_X86FastCall = 3, 1218 | CXCallingConv_X86ThisCall = 4, 1219 | CXCallingConv_X86Pascal = 5, 1220 | CXCallingConv_AAPCS = 6, 1221 | CXCallingConv_AAPCS_VFP = 7, 1222 | CXCallingConv_PnaclCall = 8, 1223 | CXCallingConv_IntelOclBicc = 9, 1224 | 1225 | CXCallingConv_Invalid = 100, 1226 | CXCallingConv_Unexposed = 200 1227 | }; 1228 | 1229 | 1230 | 1231 | typedef struct { 1232 | enum CXTypeKind kind; 1233 | void *data[2]; 1234 | } CXType; 1235 | 1236 | 1237 | CXType clang_getCursorType(CXCursor C); 1238 | 1239 | 1240 | CXString clang_getTypeSpelling(CXType CT); 1241 | 1242 | 1243 | CXType clang_getTypedefDeclUnderlyingType(CXCursor C); 1244 | 1245 | 1246 | CXType clang_getEnumDeclIntegerType(CXCursor C); 1247 | 1248 | 1249 | long long clang_getEnumConstantDeclValue(CXCursor C); 1250 | 1251 | 1252 | unsigned long long clang_getEnumConstantDeclUnsignedValue(CXCursor C); 1253 | 1254 | 1255 | int clang_getFieldDeclBitWidth(CXCursor C); 1256 | 1257 | 1258 | int clang_Cursor_getNumArguments(CXCursor C); 1259 | 1260 | 1261 | CXCursor clang_Cursor_getArgument(CXCursor C, unsigned i); 1262 | 1263 | 1264 | unsigned clang_equalTypes(CXType A, CXType B); 1265 | 1266 | 1267 | CXType clang_getCanonicalType(CXType T); 1268 | 1269 | 1270 | unsigned clang_isConstQualifiedType(CXType T); 1271 | 1272 | 1273 | unsigned clang_isVolatileQualifiedType(CXType T); 1274 | 1275 | 1276 | unsigned clang_isRestrictQualifiedType(CXType T); 1277 | 1278 | 1279 | CXType clang_getPointeeType(CXType T); 1280 | 1281 | 1282 | CXCursor clang_getTypeDeclaration(CXType T); 1283 | 1284 | 1285 | CXString clang_getDeclObjCTypeEncoding(CXCursor C); 1286 | 1287 | 1288 | CXString clang_getTypeKindSpelling(enum CXTypeKind K); 1289 | 1290 | 1291 | enum CXCallingConv clang_getFunctionTypeCallingConv(CXType T); 1292 | 1293 | 1294 | CXType clang_getResultType(CXType T); 1295 | 1296 | 1297 | int clang_getNumArgTypes(CXType T); 1298 | 1299 | 1300 | CXType clang_getArgType(CXType T, unsigned i); 1301 | 1302 | 1303 | unsigned clang_isFunctionTypeVariadic(CXType T); 1304 | 1305 | 1306 | CXType clang_getCursorResultType(CXCursor C); 1307 | 1308 | 1309 | unsigned clang_isPODType(CXType T); 1310 | 1311 | 1312 | CXType clang_getElementType(CXType T); 1313 | 1314 | 1315 | long long clang_getNumElements(CXType T); 1316 | 1317 | 1318 | CXType clang_getArrayElementType(CXType T); 1319 | 1320 | 1321 | long long clang_getArraySize(CXType T); 1322 | 1323 | 1324 | enum CXTypeLayoutError { 1325 | 1326 | CXTypeLayoutError_Invalid = -1, 1327 | 1328 | CXTypeLayoutError_Incomplete = -2, 1329 | 1330 | CXTypeLayoutError_Dependent = -3, 1331 | 1332 | CXTypeLayoutError_NotConstantSize = -4, 1333 | 1334 | CXTypeLayoutError_InvalidFieldName = -5 1335 | }; 1336 | 1337 | 1338 | long long clang_Type_getAlignOf(CXType T); 1339 | 1340 | 1341 | long long clang_Type_getSizeOf(CXType T); 1342 | 1343 | 1344 | long long clang_Type_getOffsetOf(CXType T, const char *S); 1345 | 1346 | 1347 | unsigned clang_Cursor_isBitField(CXCursor C); 1348 | 1349 | 1350 | unsigned clang_isVirtualBase(CXCursor); 1351 | 1352 | 1353 | enum CX_CXXAccessSpecifier { 1354 | CX_CXXInvalidAccessSpecifier, 1355 | CX_CXXPublic, 1356 | CX_CXXProtected, 1357 | CX_CXXPrivate 1358 | }; 1359 | 1360 | 1361 | enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(CXCursor); 1362 | 1363 | 1364 | unsigned clang_getNumOverloadedDecls(CXCursor cursor); 1365 | 1366 | 1367 | CXCursor clang_getOverloadedDecl(CXCursor cursor, 1368 | unsigned index); 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | CXType clang_getIBOutletCollectionType(CXCursor); 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | enum CXChildVisitResult { 1384 | 1385 | CXChildVisit_Break, 1386 | 1387 | CXChildVisit_Continue, 1388 | 1389 | CXChildVisit_Recurse 1390 | }; 1391 | 1392 | 1393 | typedef enum CXChildVisitResult (*CXCursorVisitor)(CXCursor cursor, 1394 | CXCursor parent, 1395 | CXClientData client_data); 1396 | 1397 | 1398 | unsigned clang_visitChildren(CXCursor parent, 1399 | CXCursorVisitor visitor, 1400 | CXClientData client_data); 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | CXString clang_getCursorUSR(CXCursor); 1408 | 1409 | 1410 | CXString clang_constructUSR_ObjCClass(const char *class_name); 1411 | 1412 | 1413 | CXString 1414 | clang_constructUSR_ObjCCategory(const char *class_name, 1415 | const char *category_name); 1416 | 1417 | 1418 | CXString 1419 | clang_constructUSR_ObjCProtocol(const char *protocol_name); 1420 | 1421 | 1422 | 1423 | CXString clang_constructUSR_ObjCIvar(const char *name, 1424 | CXString classUSR); 1425 | 1426 | 1427 | CXString clang_constructUSR_ObjCMethod(const char *name, 1428 | unsigned isInstanceMethod, 1429 | CXString classUSR); 1430 | 1431 | 1432 | CXString clang_constructUSR_ObjCProperty(const char *property, 1433 | CXString classUSR); 1434 | 1435 | 1436 | CXString clang_getCursorSpelling(CXCursor); 1437 | 1438 | 1439 | CXSourceRange clang_Cursor_getSpellingNameRange(CXCursor, 1440 | unsigned pieceIndex, 1441 | unsigned options); 1442 | 1443 | 1444 | CXString clang_getCursorDisplayName(CXCursor); 1445 | 1446 | 1447 | CXCursor clang_getCursorReferenced(CXCursor); 1448 | 1449 | 1450 | CXCursor clang_getCursorDefinition(CXCursor); 1451 | 1452 | 1453 | unsigned clang_isCursorDefinition(CXCursor); 1454 | 1455 | 1456 | CXCursor clang_getCanonicalCursor(CXCursor); 1457 | 1458 | 1459 | 1460 | int clang_Cursor_getObjCSelectorIndex(CXCursor); 1461 | 1462 | 1463 | int clang_Cursor_isDynamicCall(CXCursor C); 1464 | 1465 | 1466 | CXType clang_Cursor_getReceiverType(CXCursor C); 1467 | 1468 | 1469 | typedef enum { 1470 | CXObjCPropertyAttr_noattr = 0x00, 1471 | CXObjCPropertyAttr_readonly = 0x01, 1472 | CXObjCPropertyAttr_getter = 0x02, 1473 | CXObjCPropertyAttr_assign = 0x04, 1474 | CXObjCPropertyAttr_readwrite = 0x08, 1475 | CXObjCPropertyAttr_retain = 0x10, 1476 | CXObjCPropertyAttr_copy = 0x20, 1477 | CXObjCPropertyAttr_nonatomic = 0x40, 1478 | CXObjCPropertyAttr_setter = 0x80, 1479 | CXObjCPropertyAttr_atomic = 0x100, 1480 | CXObjCPropertyAttr_weak = 0x200, 1481 | CXObjCPropertyAttr_strong = 0x400, 1482 | CXObjCPropertyAttr_unsafe_unretained = 0x800 1483 | } CXObjCPropertyAttrKind; 1484 | 1485 | 1486 | unsigned clang_Cursor_getObjCPropertyAttributes(CXCursor C, 1487 | unsigned reserved); 1488 | 1489 | 1490 | typedef enum { 1491 | CXObjCDeclQualifier_None = 0x0, 1492 | CXObjCDeclQualifier_In = 0x1, 1493 | CXObjCDeclQualifier_Inout = 0x2, 1494 | CXObjCDeclQualifier_Out = 0x4, 1495 | CXObjCDeclQualifier_Bycopy = 0x8, 1496 | CXObjCDeclQualifier_Byref = 0x10, 1497 | CXObjCDeclQualifier_Oneway = 0x20 1498 | } CXObjCDeclQualifierKind; 1499 | 1500 | 1501 | unsigned clang_Cursor_getObjCDeclQualifiers(CXCursor C); 1502 | 1503 | 1504 | unsigned clang_Cursor_isObjCOptional(CXCursor C); 1505 | 1506 | 1507 | unsigned clang_Cursor_isVariadic(CXCursor C); 1508 | 1509 | 1510 | CXSourceRange clang_Cursor_getCommentRange(CXCursor C); 1511 | 1512 | 1513 | CXString clang_Cursor_getRawCommentText(CXCursor C); 1514 | 1515 | 1516 | CXString clang_Cursor_getBriefCommentText(CXCursor C); 1517 | 1518 | 1519 | CXComment clang_Cursor_getParsedComment(CXCursor C); 1520 | 1521 | 1522 | 1523 | 1524 | 1525 | typedef void *CXModule; 1526 | 1527 | 1528 | CXModule clang_Cursor_getModule(CXCursor C); 1529 | 1530 | 1531 | CXFile clang_Module_getASTFile(CXModule Module); 1532 | 1533 | 1534 | CXModule clang_Module_getParent(CXModule Module); 1535 | 1536 | 1537 | CXString clang_Module_getName(CXModule Module); 1538 | 1539 | 1540 | CXString clang_Module_getFullName(CXModule Module); 1541 | 1542 | 1543 | unsigned clang_Module_getNumTopLevelHeaders(CXTranslationUnit, 1544 | CXModule Module); 1545 | 1546 | 1547 | 1548 | CXFile clang_Module_getTopLevelHeader(CXTranslationUnit, 1549 | CXModule Module, unsigned Index); 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | enum CXCommentKind { 1557 | 1558 | CXComment_Null = 0, 1559 | 1560 | 1561 | CXComment_Text = 1, 1562 | 1563 | 1564 | CXComment_InlineCommand = 2, 1565 | 1566 | 1567 | CXComment_HTMLStartTag = 3, 1568 | 1569 | 1570 | CXComment_HTMLEndTag = 4, 1571 | 1572 | 1573 | CXComment_Paragraph = 5, 1574 | 1575 | 1576 | CXComment_BlockCommand = 6, 1577 | 1578 | 1579 | CXComment_ParamCommand = 7, 1580 | 1581 | 1582 | CXComment_TParamCommand = 8, 1583 | 1584 | 1585 | CXComment_VerbatimBlockCommand = 9, 1586 | 1587 | 1588 | CXComment_VerbatimBlockLine = 10, 1589 | 1590 | 1591 | CXComment_VerbatimLine = 11, 1592 | 1593 | 1594 | CXComment_FullComment = 12 1595 | }; 1596 | 1597 | 1598 | enum CXCommentInlineCommandRenderKind { 1599 | 1600 | CXCommentInlineCommandRenderKind_Normal, 1601 | 1602 | 1603 | CXCommentInlineCommandRenderKind_Bold, 1604 | 1605 | 1606 | CXCommentInlineCommandRenderKind_Monospaced, 1607 | 1608 | 1609 | CXCommentInlineCommandRenderKind_Emphasized 1610 | }; 1611 | 1612 | 1613 | enum CXCommentParamPassDirection { 1614 | 1615 | CXCommentParamPassDirection_In, 1616 | 1617 | 1618 | CXCommentParamPassDirection_Out, 1619 | 1620 | 1621 | CXCommentParamPassDirection_InOut 1622 | }; 1623 | 1624 | 1625 | enum CXCommentKind clang_Comment_getKind(CXComment Comment); 1626 | 1627 | 1628 | unsigned clang_Comment_getNumChildren(CXComment Comment); 1629 | 1630 | 1631 | 1632 | CXComment clang_Comment_getChild(CXComment Comment, unsigned ChildIdx); 1633 | 1634 | 1635 | unsigned clang_Comment_isWhitespace(CXComment Comment); 1636 | 1637 | 1638 | 1639 | unsigned clang_InlineContentComment_hasTrailingNewline(CXComment Comment); 1640 | 1641 | 1642 | CXString clang_TextComment_getText(CXComment Comment); 1643 | 1644 | 1645 | 1646 | CXString clang_InlineCommandComment_getCommandName(CXComment Comment); 1647 | 1648 | 1649 | enum CXCommentInlineCommandRenderKind 1650 | clang_InlineCommandComment_getRenderKind(CXComment Comment); 1651 | 1652 | 1653 | 1654 | unsigned clang_InlineCommandComment_getNumArgs(CXComment Comment); 1655 | 1656 | 1657 | 1658 | CXString clang_InlineCommandComment_getArgText(CXComment Comment, 1659 | unsigned ArgIdx); 1660 | 1661 | 1662 | CXString clang_HTMLTagComment_getTagName(CXComment Comment); 1663 | 1664 | 1665 | 1666 | unsigned clang_HTMLStartTagComment_isSelfClosing(CXComment Comment); 1667 | 1668 | 1669 | unsigned clang_HTMLStartTag_getNumAttrs(CXComment Comment); 1670 | 1671 | 1672 | 1673 | CXString clang_HTMLStartTag_getAttrName(CXComment Comment, unsigned AttrIdx); 1674 | 1675 | 1676 | 1677 | CXString clang_HTMLStartTag_getAttrValue(CXComment Comment, unsigned AttrIdx); 1678 | 1679 | 1680 | 1681 | CXString clang_BlockCommandComment_getCommandName(CXComment Comment); 1682 | 1683 | 1684 | 1685 | unsigned clang_BlockCommandComment_getNumArgs(CXComment Comment); 1686 | 1687 | 1688 | 1689 | CXString clang_BlockCommandComment_getArgText(CXComment Comment, 1690 | unsigned ArgIdx); 1691 | 1692 | 1693 | 1694 | CXComment clang_BlockCommandComment_getParagraph(CXComment Comment); 1695 | 1696 | 1697 | 1698 | CXString clang_ParamCommandComment_getParamName(CXComment Comment); 1699 | 1700 | 1701 | 1702 | unsigned clang_ParamCommandComment_isParamIndexValid(CXComment Comment); 1703 | 1704 | 1705 | 1706 | unsigned clang_ParamCommandComment_getParamIndex(CXComment Comment); 1707 | 1708 | 1709 | 1710 | unsigned clang_ParamCommandComment_isDirectionExplicit(CXComment Comment); 1711 | 1712 | 1713 | 1714 | enum CXCommentParamPassDirection clang_ParamCommandComment_getDirection( 1715 | CXComment Comment); 1716 | 1717 | 1718 | 1719 | CXString clang_TParamCommandComment_getParamName(CXComment Comment); 1720 | 1721 | 1722 | 1723 | unsigned clang_TParamCommandComment_isParamPositionValid(CXComment Comment); 1724 | 1725 | 1726 | 1727 | unsigned clang_TParamCommandComment_getDepth(CXComment Comment); 1728 | 1729 | 1730 | 1731 | unsigned clang_TParamCommandComment_getIndex(CXComment Comment, unsigned Depth); 1732 | 1733 | 1734 | 1735 | CXString clang_VerbatimBlockLineComment_getText(CXComment Comment); 1736 | 1737 | 1738 | CXString clang_VerbatimLineComment_getText(CXComment Comment); 1739 | 1740 | 1741 | CXString clang_HTMLTagComment_getAsString(CXComment Comment); 1742 | 1743 | 1744 | CXString clang_FullComment_getAsHTML(CXComment Comment); 1745 | 1746 | 1747 | CXString clang_FullComment_getAsXML(CXComment Comment); 1748 | 1749 | 1750 | 1751 | 1752 | 1753 | 1754 | unsigned clang_CXXMethod_isPureVirtual(CXCursor C); 1755 | 1756 | 1757 | unsigned clang_CXXMethod_isStatic(CXCursor C); 1758 | 1759 | 1760 | unsigned clang_CXXMethod_isVirtual(CXCursor C); 1761 | 1762 | 1763 | enum CXCursorKind clang_getTemplateCursorKind(CXCursor C); 1764 | 1765 | 1766 | CXCursor clang_getSpecializedCursorTemplate(CXCursor C); 1767 | 1768 | 1769 | CXSourceRange clang_getCursorReferenceNameRange(CXCursor C, 1770 | unsigned NameFlags, 1771 | unsigned PieceIndex); 1772 | 1773 | enum CXNameRefFlags { 1774 | 1775 | CXNameRange_WantQualifier = 0x1, 1776 | 1777 | 1778 | CXNameRange_WantTemplateArgs = 0x2, 1779 | 1780 | 1781 | CXNameRange_WantSinglePiece = 0x4 1782 | }; 1783 | 1784 | 1785 | 1786 | 1787 | 1788 | 1789 | typedef enum CXTokenKind { 1790 | 1791 | CXToken_Punctuation, 1792 | 1793 | 1794 | CXToken_Keyword, 1795 | 1796 | 1797 | CXToken_Identifier, 1798 | 1799 | 1800 | CXToken_Literal, 1801 | 1802 | 1803 | CXToken_Comment 1804 | } CXTokenKind; 1805 | 1806 | 1807 | typedef struct { 1808 | unsigned int_data[4]; 1809 | void *ptr_data; 1810 | } CXToken; 1811 | 1812 | 1813 | CXTokenKind clang_getTokenKind(CXToken); 1814 | 1815 | 1816 | CXString clang_getTokenSpelling(CXTranslationUnit, CXToken); 1817 | 1818 | 1819 | CXSourceLocation clang_getTokenLocation(CXTranslationUnit, 1820 | CXToken); 1821 | 1822 | 1823 | CXSourceRange clang_getTokenExtent(CXTranslationUnit, CXToken); 1824 | 1825 | 1826 | void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range, 1827 | CXToken **Tokens, unsigned *NumTokens); 1828 | 1829 | 1830 | void clang_annotateTokens(CXTranslationUnit TU, 1831 | CXToken *Tokens, unsigned NumTokens, 1832 | CXCursor *Cursors); 1833 | 1834 | 1835 | void clang_disposeTokens(CXTranslationUnit TU, 1836 | CXToken *Tokens, unsigned NumTokens); 1837 | 1838 | 1839 | 1840 | 1841 | 1842 | /* for debug/testing */ 1843 | CXString clang_getCursorKindSpelling(enum CXCursorKind Kind); 1844 | void clang_getDefinitionSpellingAndExtent(CXCursor, 1845 | const char **startBuf, 1846 | const char **endBuf, 1847 | unsigned *startLine, 1848 | unsigned *startColumn, 1849 | unsigned *endLine, 1850 | unsigned *endColumn); 1851 | void clang_enableStackTraces(void); 1852 | void clang_executeOnThread(void (*fn)(void*), void *user_data, 1853 | unsigned stack_size); 1854 | 1855 | 1856 | 1857 | 1858 | 1859 | 1860 | typedef void *CXCompletionString; 1861 | 1862 | 1863 | typedef struct { 1864 | 1865 | enum CXCursorKind CursorKind; 1866 | 1867 | 1868 | CXCompletionString CompletionString; 1869 | } CXCompletionResult; 1870 | 1871 | 1872 | enum CXCompletionChunkKind { 1873 | 1874 | CXCompletionChunk_Optional, 1875 | 1876 | CXCompletionChunk_TypedText, 1877 | 1878 | CXCompletionChunk_Text, 1879 | 1880 | CXCompletionChunk_Placeholder, 1881 | 1882 | CXCompletionChunk_Informative, 1883 | 1884 | CXCompletionChunk_CurrentParameter, 1885 | 1886 | CXCompletionChunk_LeftParen, 1887 | 1888 | CXCompletionChunk_RightParen, 1889 | 1890 | CXCompletionChunk_LeftBracket, 1891 | 1892 | CXCompletionChunk_RightBracket, 1893 | 1894 | CXCompletionChunk_LeftBrace, 1895 | 1896 | CXCompletionChunk_RightBrace, 1897 | 1898 | CXCompletionChunk_LeftAngle, 1899 | 1900 | CXCompletionChunk_RightAngle, 1901 | 1902 | CXCompletionChunk_Comma, 1903 | 1904 | CXCompletionChunk_ResultType, 1905 | 1906 | CXCompletionChunk_Colon, 1907 | 1908 | CXCompletionChunk_SemiColon, 1909 | 1910 | CXCompletionChunk_Equal, 1911 | 1912 | CXCompletionChunk_HorizontalSpace, 1913 | 1914 | CXCompletionChunk_VerticalSpace 1915 | }; 1916 | 1917 | 1918 | enum CXCompletionChunkKind 1919 | clang_getCompletionChunkKind(CXCompletionString completion_string, 1920 | unsigned chunk_number); 1921 | 1922 | 1923 | CXString 1924 | clang_getCompletionChunkText(CXCompletionString completion_string, 1925 | unsigned chunk_number); 1926 | 1927 | 1928 | CXCompletionString 1929 | clang_getCompletionChunkCompletionString(CXCompletionString completion_string, 1930 | unsigned chunk_number); 1931 | 1932 | 1933 | unsigned 1934 | clang_getNumCompletionChunks(CXCompletionString completion_string); 1935 | 1936 | 1937 | unsigned 1938 | clang_getCompletionPriority(CXCompletionString completion_string); 1939 | 1940 | 1941 | enum CXAvailabilityKind 1942 | clang_getCompletionAvailability(CXCompletionString completion_string); 1943 | 1944 | 1945 | unsigned 1946 | clang_getCompletionNumAnnotations(CXCompletionString completion_string); 1947 | 1948 | 1949 | CXString 1950 | clang_getCompletionAnnotation(CXCompletionString completion_string, 1951 | unsigned annotation_number); 1952 | 1953 | 1954 | CXString 1955 | clang_getCompletionParent(CXCompletionString completion_string, 1956 | enum CXCursorKind *kind); 1957 | 1958 | 1959 | CXString 1960 | clang_getCompletionBriefComment(CXCompletionString completion_string); 1961 | 1962 | 1963 | CXCompletionString 1964 | clang_getCursorCompletionString(CXCursor cursor); 1965 | 1966 | 1967 | typedef struct { 1968 | 1969 | CXCompletionResult *Results; 1970 | 1971 | 1972 | unsigned NumResults; 1973 | } CXCodeCompleteResults; 1974 | 1975 | 1976 | enum CXCodeComplete_Flags { 1977 | 1978 | CXCodeComplete_IncludeMacros = 0x01, 1979 | 1980 | 1981 | CXCodeComplete_IncludeCodePatterns = 0x02, 1982 | 1983 | 1984 | CXCodeComplete_IncludeBriefComments = 0x04 1985 | }; 1986 | 1987 | 1988 | enum CXCompletionContext { 1989 | 1990 | CXCompletionContext_Unexposed = 0, 1991 | 1992 | 1993 | CXCompletionContext_AnyType = 1 << 0, 1994 | 1995 | 1996 | CXCompletionContext_AnyValue = 1 << 1, 1997 | 1998 | CXCompletionContext_ObjCObjectValue = 1 << 2, 1999 | 2000 | CXCompletionContext_ObjCSelectorValue = 1 << 3, 2001 | 2002 | CXCompletionContext_CXXClassTypeValue = 1 << 4, 2003 | 2004 | 2005 | CXCompletionContext_DotMemberAccess = 1 << 5, 2006 | 2007 | CXCompletionContext_ArrowMemberAccess = 1 << 6, 2008 | 2009 | CXCompletionContext_ObjCPropertyAccess = 1 << 7, 2010 | 2011 | 2012 | CXCompletionContext_EnumTag = 1 << 8, 2013 | 2014 | CXCompletionContext_UnionTag = 1 << 9, 2015 | 2016 | CXCompletionContext_StructTag = 1 << 10, 2017 | 2018 | 2019 | CXCompletionContext_ClassTag = 1 << 11, 2020 | 2021 | CXCompletionContext_Namespace = 1 << 12, 2022 | 2023 | CXCompletionContext_NestedNameSpecifier = 1 << 13, 2024 | 2025 | 2026 | CXCompletionContext_ObjCInterface = 1 << 14, 2027 | 2028 | CXCompletionContext_ObjCProtocol = 1 << 15, 2029 | 2030 | CXCompletionContext_ObjCCategory = 1 << 16, 2031 | 2032 | CXCompletionContext_ObjCInstanceMessage = 1 << 17, 2033 | 2034 | CXCompletionContext_ObjCClassMessage = 1 << 18, 2035 | 2036 | CXCompletionContext_ObjCSelectorName = 1 << 19, 2037 | 2038 | 2039 | CXCompletionContext_MacroName = 1 << 20, 2040 | 2041 | 2042 | CXCompletionContext_NaturalLanguage = 1 << 21, 2043 | 2044 | 2045 | CXCompletionContext_Unknown = ((1 << 22) - 1) 2046 | }; 2047 | 2048 | 2049 | unsigned clang_defaultCodeCompleteOptions(void); 2050 | 2051 | 2052 | 2053 | CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, 2054 | const char *complete_filename, 2055 | unsigned complete_line, 2056 | unsigned complete_column, 2057 | struct CXUnsavedFile *unsaved_files, 2058 | unsigned num_unsaved_files, 2059 | unsigned options); 2060 | 2061 | 2062 | 2063 | void clang_sortCodeCompletionResults(CXCompletionResult *Results, 2064 | unsigned NumResults); 2065 | 2066 | 2067 | 2068 | void clang_disposeCodeCompleteResults(CXCodeCompleteResults *Results); 2069 | 2070 | 2071 | 2072 | unsigned clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *Results); 2073 | 2074 | 2075 | 2076 | CXDiagnostic clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *Results, 2077 | unsigned Index); 2078 | 2079 | 2080 | 2081 | unsigned long long clang_codeCompleteGetContexts( 2082 | CXCodeCompleteResults *Results); 2083 | 2084 | 2085 | 2086 | enum CXCursorKind clang_codeCompleteGetContainerKind( 2087 | CXCodeCompleteResults *Results, 2088 | unsigned *IsIncomplete); 2089 | 2090 | 2091 | 2092 | CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *Results); 2093 | 2094 | 2095 | 2096 | 2097 | CXString clang_codeCompleteGetObjCSelector(CXCodeCompleteResults *Results); 2098 | 2099 | 2100 | 2101 | 2102 | 2103 | 2104 | 2105 | CXString clang_getClangVersion(void); 2106 | 2107 | 2108 | 2109 | void clang_toggleCrashRecovery(unsigned isEnabled); 2110 | 2111 | 2112 | typedef void (*CXInclusionVisitor)(CXFile included_file, 2113 | CXSourceLocation* inclusion_stack, 2114 | unsigned include_len, 2115 | CXClientData client_data); 2116 | 2117 | 2118 | void clang_getInclusions(CXTranslationUnit tu, 2119 | CXInclusionVisitor visitor, 2120 | CXClientData client_data); 2121 | 2122 | 2123 | 2124 | 2125 | 2126 | 2127 | typedef void *CXRemapping; 2128 | 2129 | 2130 | CXRemapping clang_getRemappings(const char *path); 2131 | 2132 | 2133 | 2134 | CXRemapping clang_getRemappingsFromFileList(const char **filePaths, 2135 | unsigned numFiles); 2136 | 2137 | 2138 | unsigned clang_remap_getNumFiles(CXRemapping); 2139 | 2140 | 2141 | void clang_remap_getFilenames(CXRemapping, unsigned index, 2142 | CXString *original, CXString *transformed); 2143 | 2144 | 2145 | void clang_remap_dispose(CXRemapping); 2146 | 2147 | 2148 | 2149 | 2150 | 2151 | enum CXVisitorResult { 2152 | CXVisit_Break, 2153 | CXVisit_Continue 2154 | }; 2155 | 2156 | typedef struct { 2157 | void *context; 2158 | enum CXVisitorResult (*visit)(void *context, CXCursor, CXSourceRange); 2159 | } CXCursorAndRangeVisitor; 2160 | 2161 | typedef enum { 2162 | 2163 | CXResult_Success = 0, 2164 | 2165 | CXResult_Invalid = 1, 2166 | 2167 | CXResult_VisitBreak = 2 2168 | 2169 | } CXResult; 2170 | 2171 | 2172 | CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file, 2173 | CXCursorAndRangeVisitor visitor); 2174 | 2175 | 2176 | CXResult clang_findIncludesInFile(CXTranslationUnit TU, 2177 | CXFile file, 2178 | CXCursorAndRangeVisitor visitor); 2179 | 2180 | 2181 | 2182 | typedef void *CXIdxClientFile; 2183 | 2184 | 2185 | typedef void *CXIdxClientEntity; 2186 | 2187 | 2188 | typedef void *CXIdxClientContainer; 2189 | 2190 | 2191 | typedef void *CXIdxClientASTFile; 2192 | 2193 | 2194 | typedef struct { 2195 | void *ptr_data[2]; 2196 | unsigned int_data; 2197 | } CXIdxLoc; 2198 | 2199 | 2200 | typedef struct { 2201 | 2202 | CXIdxLoc hashLoc; 2203 | 2204 | const char *filename; 2205 | 2206 | CXFile file; 2207 | int isImport; 2208 | int isAngled; 2209 | 2210 | int isModuleImport; 2211 | } CXIdxIncludedFileInfo; 2212 | 2213 | 2214 | typedef struct { 2215 | 2216 | CXFile file; 2217 | 2218 | CXModule module; 2219 | 2220 | CXIdxLoc loc; 2221 | 2222 | int isImplicit; 2223 | 2224 | } CXIdxImportedASTFileInfo; 2225 | 2226 | typedef enum { 2227 | CXIdxEntity_Unexposed = 0, 2228 | CXIdxEntity_Typedef = 1, 2229 | CXIdxEntity_Function = 2, 2230 | CXIdxEntity_Variable = 3, 2231 | CXIdxEntity_Field = 4, 2232 | CXIdxEntity_EnumConstant = 5, 2233 | 2234 | CXIdxEntity_ObjCClass = 6, 2235 | CXIdxEntity_ObjCProtocol = 7, 2236 | CXIdxEntity_ObjCCategory = 8, 2237 | 2238 | CXIdxEntity_ObjCInstanceMethod = 9, 2239 | CXIdxEntity_ObjCClassMethod = 10, 2240 | CXIdxEntity_ObjCProperty = 11, 2241 | CXIdxEntity_ObjCIvar = 12, 2242 | 2243 | CXIdxEntity_Enum = 13, 2244 | CXIdxEntity_Struct = 14, 2245 | CXIdxEntity_Union = 15, 2246 | 2247 | CXIdxEntity_CXXClass = 16, 2248 | CXIdxEntity_CXXNamespace = 17, 2249 | CXIdxEntity_CXXNamespaceAlias = 18, 2250 | CXIdxEntity_CXXStaticVariable = 19, 2251 | CXIdxEntity_CXXStaticMethod = 20, 2252 | CXIdxEntity_CXXInstanceMethod = 21, 2253 | CXIdxEntity_CXXConstructor = 22, 2254 | CXIdxEntity_CXXDestructor = 23, 2255 | CXIdxEntity_CXXConversionFunction = 24, 2256 | CXIdxEntity_CXXTypeAlias = 25, 2257 | CXIdxEntity_CXXInterface = 26 2258 | 2259 | } CXIdxEntityKind; 2260 | 2261 | typedef enum { 2262 | CXIdxEntityLang_None = 0, 2263 | CXIdxEntityLang_C = 1, 2264 | CXIdxEntityLang_ObjC = 2, 2265 | CXIdxEntityLang_CXX = 3 2266 | } CXIdxEntityLanguage; 2267 | 2268 | 2269 | typedef enum { 2270 | CXIdxEntity_NonTemplate = 0, 2271 | CXIdxEntity_Template = 1, 2272 | CXIdxEntity_TemplatePartialSpecialization = 2, 2273 | CXIdxEntity_TemplateSpecialization = 3 2274 | } CXIdxEntityCXXTemplateKind; 2275 | 2276 | typedef enum { 2277 | CXIdxAttr_Unexposed = 0, 2278 | CXIdxAttr_IBAction = 1, 2279 | CXIdxAttr_IBOutlet = 2, 2280 | CXIdxAttr_IBOutletCollection = 3 2281 | } CXIdxAttrKind; 2282 | 2283 | typedef struct { 2284 | CXIdxAttrKind kind; 2285 | CXCursor cursor; 2286 | CXIdxLoc loc; 2287 | } CXIdxAttrInfo; 2288 | 2289 | typedef struct { 2290 | CXIdxEntityKind kind; 2291 | CXIdxEntityCXXTemplateKind templateKind; 2292 | CXIdxEntityLanguage lang; 2293 | const char *name; 2294 | const char *USR; 2295 | CXCursor cursor; 2296 | const CXIdxAttrInfo *const *attributes; 2297 | unsigned numAttributes; 2298 | } CXIdxEntityInfo; 2299 | 2300 | typedef struct { 2301 | CXCursor cursor; 2302 | } CXIdxContainerInfo; 2303 | 2304 | typedef struct { 2305 | const CXIdxAttrInfo *attrInfo; 2306 | const CXIdxEntityInfo *objcClass; 2307 | CXCursor classCursor; 2308 | CXIdxLoc classLoc; 2309 | } CXIdxIBOutletCollectionAttrInfo; 2310 | 2311 | typedef enum { 2312 | CXIdxDeclFlag_Skipped = 0x1 2313 | } CXIdxDeclInfoFlags; 2314 | 2315 | typedef struct { 2316 | const CXIdxEntityInfo *entityInfo; 2317 | CXCursor cursor; 2318 | CXIdxLoc loc; 2319 | const CXIdxContainerInfo *semanticContainer; 2320 | 2321 | const CXIdxContainerInfo *lexicalContainer; 2322 | int isRedeclaration; 2323 | int isDefinition; 2324 | int isContainer; 2325 | const CXIdxContainerInfo *declAsContainer; 2326 | 2327 | int isImplicit; 2328 | const CXIdxAttrInfo *const *attributes; 2329 | unsigned numAttributes; 2330 | 2331 | unsigned flags; 2332 | 2333 | } CXIdxDeclInfo; 2334 | 2335 | typedef enum { 2336 | CXIdxObjCContainer_ForwardRef = 0, 2337 | CXIdxObjCContainer_Interface = 1, 2338 | CXIdxObjCContainer_Implementation = 2 2339 | } CXIdxObjCContainerKind; 2340 | 2341 | typedef struct { 2342 | const CXIdxDeclInfo *declInfo; 2343 | CXIdxObjCContainerKind kind; 2344 | } CXIdxObjCContainerDeclInfo; 2345 | 2346 | typedef struct { 2347 | const CXIdxEntityInfo *base; 2348 | CXCursor cursor; 2349 | CXIdxLoc loc; 2350 | } CXIdxBaseClassInfo; 2351 | 2352 | typedef struct { 2353 | const CXIdxEntityInfo *protocol; 2354 | CXCursor cursor; 2355 | CXIdxLoc loc; 2356 | } CXIdxObjCProtocolRefInfo; 2357 | 2358 | typedef struct { 2359 | const CXIdxObjCProtocolRefInfo *const *protocols; 2360 | unsigned numProtocols; 2361 | } CXIdxObjCProtocolRefListInfo; 2362 | 2363 | typedef struct { 2364 | const CXIdxObjCContainerDeclInfo *containerInfo; 2365 | const CXIdxBaseClassInfo *superInfo; 2366 | const CXIdxObjCProtocolRefListInfo *protocols; 2367 | } CXIdxObjCInterfaceDeclInfo; 2368 | 2369 | typedef struct { 2370 | const CXIdxObjCContainerDeclInfo *containerInfo; 2371 | const CXIdxEntityInfo *objcClass; 2372 | CXCursor classCursor; 2373 | CXIdxLoc classLoc; 2374 | const CXIdxObjCProtocolRefListInfo *protocols; 2375 | } CXIdxObjCCategoryDeclInfo; 2376 | 2377 | typedef struct { 2378 | const CXIdxDeclInfo *declInfo; 2379 | const CXIdxEntityInfo *getter; 2380 | const CXIdxEntityInfo *setter; 2381 | } CXIdxObjCPropertyDeclInfo; 2382 | 2383 | typedef struct { 2384 | const CXIdxDeclInfo *declInfo; 2385 | const CXIdxBaseClassInfo *const *bases; 2386 | unsigned numBases; 2387 | } CXIdxCXXClassDeclInfo; 2388 | 2389 | 2390 | typedef enum { 2391 | 2392 | CXIdxEntityRef_Direct = 1, 2393 | 2394 | CXIdxEntityRef_Implicit = 2 2395 | } CXIdxEntityRefKind; 2396 | 2397 | 2398 | typedef struct { 2399 | CXIdxEntityRefKind kind; 2400 | 2401 | CXCursor cursor; 2402 | CXIdxLoc loc; 2403 | 2404 | const CXIdxEntityInfo *referencedEntity; 2405 | 2406 | const CXIdxEntityInfo *parentEntity; 2407 | 2408 | const CXIdxContainerInfo *container; 2409 | } CXIdxEntityRefInfo; 2410 | 2411 | 2412 | typedef struct { 2413 | 2414 | int (*abortQuery)(CXClientData client_data, void *reserved); 2415 | 2416 | 2417 | void (*diagnostic)(CXClientData client_data, 2418 | CXDiagnosticSet, void *reserved); 2419 | 2420 | CXIdxClientFile (*enteredMainFile)(CXClientData client_data, 2421 | CXFile mainFile, void *reserved); 2422 | 2423 | 2424 | CXIdxClientFile (*ppIncludedFile)(CXClientData client_data, 2425 | const CXIdxIncludedFileInfo *); 2426 | 2427 | 2428 | CXIdxClientASTFile (*importedASTFile)(CXClientData client_data, 2429 | const CXIdxImportedASTFileInfo *); 2430 | 2431 | 2432 | CXIdxClientContainer (*startedTranslationUnit)(CXClientData client_data, 2433 | void *reserved); 2434 | 2435 | void (*indexDeclaration)(CXClientData client_data, 2436 | const CXIdxDeclInfo *); 2437 | 2438 | 2439 | void (*indexEntityReference)(CXClientData client_data, 2440 | const CXIdxEntityRefInfo *); 2441 | 2442 | } IndexerCallbacks; 2443 | 2444 | int clang_index_isEntityObjCContainerKind(CXIdxEntityKind); 2445 | const CXIdxObjCContainerDeclInfo * 2446 | clang_index_getObjCContainerDeclInfo(const CXIdxDeclInfo *); 2447 | 2448 | const CXIdxObjCInterfaceDeclInfo * 2449 | clang_index_getObjCInterfaceDeclInfo(const CXIdxDeclInfo *); 2450 | 2451 | 2452 | const CXIdxObjCCategoryDeclInfo * 2453 | clang_index_getObjCCategoryDeclInfo(const CXIdxDeclInfo *); 2454 | 2455 | const CXIdxObjCProtocolRefListInfo * 2456 | clang_index_getObjCProtocolRefListInfo(const CXIdxDeclInfo *); 2457 | 2458 | const CXIdxObjCPropertyDeclInfo * 2459 | clang_index_getObjCPropertyDeclInfo(const CXIdxDeclInfo *); 2460 | 2461 | const CXIdxIBOutletCollectionAttrInfo * 2462 | clang_index_getIBOutletCollectionAttrInfo(const CXIdxAttrInfo *); 2463 | 2464 | const CXIdxCXXClassDeclInfo * 2465 | clang_index_getCXXClassDeclInfo(const CXIdxDeclInfo *); 2466 | 2467 | 2468 | CXIdxClientContainer 2469 | clang_index_getClientContainer(const CXIdxContainerInfo *); 2470 | 2471 | 2472 | void 2473 | clang_index_setClientContainer(const CXIdxContainerInfo *,CXIdxClientContainer); 2474 | 2475 | 2476 | CXIdxClientEntity 2477 | clang_index_getClientEntity(const CXIdxEntityInfo *); 2478 | 2479 | 2480 | void 2481 | clang_index_setClientEntity(const CXIdxEntityInfo *, CXIdxClientEntity); 2482 | 2483 | 2484 | typedef void *CXIndexAction; 2485 | 2486 | 2487 | CXIndexAction clang_IndexAction_create(CXIndex CIdx); 2488 | 2489 | 2490 | void clang_IndexAction_dispose(CXIndexAction); 2491 | 2492 | typedef enum { 2493 | 2494 | CXIndexOpt_None = 0x0, 2495 | 2496 | 2497 | CXIndexOpt_SuppressRedundantRefs = 0x1, 2498 | 2499 | 2500 | CXIndexOpt_IndexFunctionLocalSymbols = 0x2, 2501 | 2502 | 2503 | CXIndexOpt_IndexImplicitTemplateInstantiations = 0x4, 2504 | 2505 | 2506 | CXIndexOpt_SuppressWarnings = 0x8, 2507 | 2508 | 2509 | CXIndexOpt_SkipParsedBodiesInSession = 0x10 2510 | 2511 | } CXIndexOptFlags; 2512 | 2513 | 2514 | int clang_indexSourceFile(CXIndexAction, 2515 | CXClientData client_data, 2516 | IndexerCallbacks *index_callbacks, 2517 | unsigned index_callbacks_size, 2518 | unsigned index_options, 2519 | const char *source_filename, 2520 | const char * const *command_line_args, 2521 | int num_command_line_args, 2522 | struct CXUnsavedFile *unsaved_files, 2523 | unsigned num_unsaved_files, 2524 | CXTranslationUnit *out_TU, 2525 | unsigned TU_options); 2526 | 2527 | 2528 | int clang_indexTranslationUnit(CXIndexAction, 2529 | CXClientData client_data, 2530 | IndexerCallbacks *index_callbacks, 2531 | unsigned index_callbacks_size, 2532 | unsigned index_options, 2533 | CXTranslationUnit); 2534 | 2535 | 2536 | void clang_indexLoc_getFileLocation(CXIdxLoc loc, 2537 | CXIdxClientFile *indexFile, 2538 | CXFile *file, 2539 | unsigned *line, 2540 | unsigned *column, 2541 | unsigned *offset); 2542 | 2543 | 2544 | 2545 | CXSourceLocation clang_indexLoc_getCXSourceLocation(CXIdxLoc loc); 2546 | 2547 | 2548 | 2549 | 2550 | 2551 | 2552 | ]==========] 2553 | -------------------------------------------------------------------------------- /ljclang/ljclang_cursor_kind.lua: -------------------------------------------------------------------------------- 1 | return { name={ 2 | [1] = "UnexposedDecl"; 3 | [2] = "StructDecl"; 4 | [3] = "UnionDecl"; 5 | [4] = "ClassDecl"; 6 | [5] = "EnumDecl"; 7 | [6] = "FieldDecl"; 8 | [7] = "EnumConstantDecl"; 9 | [8] = "FunctionDecl"; 10 | [9] = "VarDecl"; 11 | [10] = "ParmDecl"; 12 | [11] = "ObjCInterfaceDecl"; 13 | [12] = "ObjCCategoryDecl"; 14 | [13] = "ObjCProtocolDecl"; 15 | [14] = "ObjCPropertyDecl"; 16 | [15] = "ObjCIvarDecl"; 17 | [16] = "ObjCInstanceMethodDecl"; 18 | [17] = "ObjCClassMethodDecl"; 19 | [18] = "ObjCImplementationDecl"; 20 | [19] = "ObjCCategoryImplDecl"; 21 | [20] = "TypedefDecl"; 22 | [21] = "CXXMethod"; 23 | [22] = "Namespace"; 24 | [23] = "LinkageSpec"; 25 | [24] = "Constructor"; 26 | [25] = "Destructor"; 27 | [26] = "ConversionFunction"; 28 | [27] = "TemplateTypeParameter"; 29 | [28] = "NonTypeTemplateParameter"; 30 | [29] = "TemplateTemplateParameter"; 31 | [30] = "FunctionTemplate"; 32 | [31] = "ClassTemplate"; 33 | [32] = "ClassTemplatePartialSpecialization"; 34 | [33] = "NamespaceAlias"; 35 | [34] = "UsingDirective"; 36 | [35] = "UsingDeclaration"; 37 | [36] = "TypeAliasDecl"; 38 | [37] = "ObjCSynthesizeDecl"; 39 | [38] = "ObjCDynamicDecl"; 40 | [39] = "CXXAccessSpecifier"; 41 | [40] = "ObjCSuperClassRef"; 42 | [41] = "ObjCProtocolRef"; 43 | [42] = "ObjCClassRef"; 44 | [43] = "TypeRef"; 45 | [44] = "CXXBaseSpecifier"; 46 | [45] = "TemplateRef"; 47 | [46] = "NamespaceRef"; 48 | [47] = "MemberRef"; 49 | [48] = "LabelRef"; 50 | [49] = "OverloadedDeclRef"; 51 | [50] = "VariableRef"; 52 | [70] = "InvalidFile"; 53 | [71] = "NoDeclFound"; 54 | [72] = "NotImplemented"; 55 | [73] = "InvalidCode"; 56 | [100] = "UnexposedExpr"; 57 | [101] = "DeclRefExpr"; 58 | [102] = "MemberRefExpr"; 59 | [103] = "CallExpr"; 60 | [104] = "ObjCMessageExpr"; 61 | [105] = "BlockExpr"; 62 | [106] = "IntegerLiteral"; 63 | [107] = "FloatingLiteral"; 64 | [108] = "ImaginaryLiteral"; 65 | [109] = "StringLiteral"; 66 | [110] = "CharacterLiteral"; 67 | [111] = "ParenExpr"; 68 | [112] = "UnaryOperator"; 69 | [113] = "ArraySubscriptExpr"; 70 | [114] = "BinaryOperator"; 71 | [115] = "CompoundAssignOperator"; 72 | [116] = "ConditionalOperator"; 73 | [117] = "CStyleCastExpr"; 74 | [118] = "CompoundLiteralExpr"; 75 | [119] = "InitListExpr"; 76 | [120] = "AddrLabelExpr"; 77 | [121] = "StmtExpr"; 78 | [122] = "GenericSelectionExpr"; 79 | [123] = "GNUNullExpr"; 80 | [124] = "CXXStaticCastExpr"; 81 | [125] = "CXXDynamicCastExpr"; 82 | [126] = "CXXReinterpretCastExpr"; 83 | [127] = "CXXConstCastExpr"; 84 | [128] = "CXXFunctionalCastExpr"; 85 | [129] = "CXXTypeidExpr"; 86 | [130] = "CXXBoolLiteralExpr"; 87 | [131] = "CXXNullPtrLiteralExpr"; 88 | [132] = "CXXThisExpr"; 89 | [133] = "CXXThrowExpr"; 90 | [134] = "CXXNewExpr"; 91 | [135] = "CXXDeleteExpr"; 92 | [136] = "UnaryExpr"; 93 | [137] = "ObjCStringLiteral"; 94 | [138] = "ObjCEncodeExpr"; 95 | [139] = "ObjCSelectorExpr"; 96 | [140] = "ObjCProtocolExpr"; 97 | [141] = "ObjCBridgedCastExpr"; 98 | [142] = "PackExpansionExpr"; 99 | [143] = "SizeOfPackExpr"; 100 | [144] = "LambdaExpr"; 101 | [145] = "ObjCBoolLiteralExpr"; 102 | [146] = "ObjCSelfExpr"; 103 | [200] = "UnexposedStmt"; 104 | [201] = "LabelStmt"; 105 | [202] = "CompoundStmt"; 106 | [203] = "CaseStmt"; 107 | [204] = "DefaultStmt"; 108 | [205] = "IfStmt"; 109 | [206] = "SwitchStmt"; 110 | [207] = "WhileStmt"; 111 | [208] = "DoStmt"; 112 | [209] = "ForStmt"; 113 | [210] = "GotoStmt"; 114 | [211] = "IndirectGotoStmt"; 115 | [212] = "ContinueStmt"; 116 | [213] = "BreakStmt"; 117 | [214] = "ReturnStmt"; 118 | [215] = "AsmStmt"; 119 | [216] = "ObjCAtTryStmt"; 120 | [217] = "ObjCAtCatchStmt"; 121 | [218] = "ObjCAtFinallyStmt"; 122 | [219] = "ObjCAtThrowStmt"; 123 | [220] = "ObjCAtSynchronizedStmt"; 124 | [221] = "ObjCAutoreleasePoolStmt"; 125 | [222] = "ObjCForCollectionStmt"; 126 | [223] = "CXXCatchStmt"; 127 | [224] = "CXXTryStmt"; 128 | [225] = "CXXForRangeStmt"; 129 | [226] = "SEHTryStmt"; 130 | [227] = "SEHExceptStmt"; 131 | [228] = "SEHFinallyStmt"; 132 | [229] = "MSAsmStmt"; 133 | [230] = "NullStmt"; 134 | [231] = "DeclStmt"; 135 | [232] = "OMPParallelDirective"; 136 | [300] = "TranslationUnit"; 137 | [400] = "UnexposedAttr"; 138 | [401] = "IBActionAttr"; 139 | [402] = "IBOutletAttr"; 140 | [403] = "IBOutletCollectionAttr"; 141 | [404] = "CXXFinalAttr"; 142 | [405] = "CXXOverrideAttr"; 143 | [406] = "AnnotateAttr"; 144 | [407] = "AsmLabelAttr"; 145 | [408] = "PackedAttr"; 146 | [500] = "PreprocessingDirective"; 147 | [501] = "MacroDefinition"; 148 | [502] = "MacroExpansion"; 149 | [503] = "InclusionDirective"; 150 | [600] = "ModuleImportDecl"; 151 | }, } 152 | -------------------------------------------------------------------------------- /ljclang/ljclang_support.c: -------------------------------------------------------------------------------- 1 | /* Support library for LJClang, for cases the LuaJIT FFI doesn't handle. 2 | * (Mostly C callbacks with pass-by-value compound arguments/results.) */ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | 10 | /* Our cursor visitor takes the CXCursor objects by pointer. */ 11 | typedef enum CXChildVisitResult (*LJCX_CursorVisitor)( 12 | CXCursor *cursor, CXCursor *parent, CXClientData client_data); 13 | 14 | /* Compile with cursor kinds bitmap? This was originally intended as 15 | * 'optimization' (because C callback are said to be slow in LuaJIT), but maybe 16 | * it was premature optimization... they're still pretty fast. */ 17 | /*#define USE_KINDS_BITAR*/ 18 | 19 | #define UNUSED(var) (void)(var); 20 | 21 | /* NOTE: Make sure that the 'last' CXCursor kind LAST_KIND is the numerically 22 | * greatest. */ 23 | #define LAST_KIND CXCursor_LastExtraDecl 24 | #define NUM_KINDS (LAST_KIND+1) 25 | #define KINDS_BITAR_SIZE ((NUM_KINDS+7)>>3) 26 | 27 | typedef struct { 28 | LJCX_CursorVisitor visitor; 29 | #ifdef USE_KINDS_BITAR 30 | unsigned char kinds[KINDS_BITAR_SIZE]; 31 | #endif 32 | } LJCX_CursorVisitorData; 33 | 34 | static LJCX_CursorVisitorData *g_cursorVisitors; 35 | static unsigned g_numVisitors; 36 | 37 | 38 | /* Registers a LJClang_CursorVisitor callback and returns an index by 39 | * which it can be subsequently referenced. 40 | * 41 | * : An array containing CXCursorKind values. If contains a 42 | * particular cursor kind, is invoked on such a cursor. 43 | * NOTE: Only effective if compiled with #define USE_KINDS_BITAR. 44 | * 45 | * : The number of elements in . In the special case of being 46 | * 0, all cursor kinds are considered to be visited and is not 47 | * accessed. 48 | * 49 | * Returns: 50 | * >=0: the visitor function index on success. 51 | * -1: failed realloc(). 52 | * -2: contains an invalid CXCursorKind 53 | */ 54 | int ljclang_regCursorVisitor(LJCX_CursorVisitor visitor, enum CXCursorKind *kinds, int numkinds) 55 | { 56 | #ifdef USE_KINDS_BITAR 57 | int i; 58 | #endif 59 | const int szCVD = (int)sizeof(LJCX_CursorVisitorData); 60 | LJCX_CursorVisitorData cvd; 61 | 62 | memset(&cvd, 0, szCVD); 63 | cvd.visitor = visitor; 64 | 65 | #ifdef USE_KINDS_BITAR 66 | if (numkinds == 0) 67 | memset(cvd.kinds, 255, sizeof(cvd.kinds)); 68 | 69 | for (i=0; i= NUM_KINDS) 72 | return -2; 73 | 74 | cvd.kinds[i>>3] |= 1<<(i&7); 75 | } 76 | #else 77 | UNUSED(kinds); 78 | UNUSED(numkinds); 79 | #endif 80 | 81 | { 82 | /* Finally, reallocate g_cursorVisitors. */ 83 | void *newVisitors = realloc(g_cursorVisitors, (g_numVisitors+1)*szCVD); 84 | 85 | if (newVisitors == NULL) 86 | return -1; 87 | 88 | g_cursorVisitors = (LJCX_CursorVisitorData *)newVisitors; 89 | memcpy(&g_cursorVisitors[g_numVisitors], &cvd, szCVD); 90 | 91 | return g_numVisitors++; 92 | } 93 | } 94 | 95 | static enum CXChildVisitResult 96 | ourCursorVisitor(CXCursor cursor, CXCursor parent, CXClientData client_data) 97 | { 98 | LJCX_CursorVisitorData *cvd = (LJCX_CursorVisitorData *)client_data; 99 | 100 | int k = cursor.kind; 101 | 102 | if ((unsigned)k >= NUM_KINDS) 103 | return CXChildVisit_Break; 104 | #ifdef USE_KINDS_BITAR 105 | if (cvd->kinds[k>>3] & (1<<(k&7))) 106 | #endif 107 | return cvd->visitor(&cursor, &parent, NULL); 108 | 109 | return CXChildVisit_Continue; 110 | } 111 | 112 | int ljclang_visitChildren(CXCursor parent, int visitoridx) 113 | { 114 | unsigned wasbroken; 115 | 116 | if ((unsigned)visitoridx >= g_numVisitors) 117 | return -1; 118 | 119 | wasbroken = clang_visitChildren(parent, ourCursorVisitor, 120 | (CXClientData)&g_cursorVisitors[visitoridx]); 121 | return (wasbroken ? 1 : 0); 122 | } 123 | -------------------------------------------------------------------------------- /ljclang/mgrep.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | -- mgrep.lua -- Search for named member accesses. 3 | 4 | local io = require("io") 5 | local os = require("os") 6 | local table = require("table") 7 | 8 | local cl = require("ljclang") 9 | 10 | local print = print 11 | 12 | ---------- 13 | 14 | local function printf(fmt, ...) 15 | print(string.format(fmt, ...)) 16 | end 17 | 18 | local function errprintf(fmt, ...) 19 | io.stderr:write(string.format(fmt, ...).."\n") 20 | end 21 | 22 | local function usage(hline) 23 | if (hline) then 24 | print(hline) 25 | end 26 | print("Usage: "..arg[0].." -t -m -O '' [options...] ...") 27 | print(" -n: only parse and potentially print diagnostics") 28 | print(" -q: be quiet (don't print diagnostics)") 29 | os.exit(1) 30 | end 31 | 32 | 33 | local parsecmdline = require("parsecmdline_pk") 34 | local opt_meta = { t=true, m=true, O=true, q=false, n=false, } 35 | 36 | local opts, files = parsecmdline.getopts(opt_meta, arg, usage) 37 | 38 | local typedefName = opts.t 39 | local memberName = opts.m 40 | local clangOpts = opts.O 41 | local quiet = opts.q 42 | local dryrun = opts.n 43 | 44 | if (not typedefName or not memberName) then 45 | usage("Must provide -t and -m options") 46 | end 47 | 48 | local V = cl.ChildVisitResult 49 | 50 | local g_curFileName 51 | -- The StructDecl of the type we're searching for. 52 | local g_structDecl 53 | 54 | local visitorGetType = cl.regCursorVisitor( 55 | function(cur, parent) 56 | if (cur:haskind("TypedefDecl")) then 57 | local typ = cur:typedefType() 58 | local structDecl = typ:declaration() 59 | if (structDecl:haskind("StructDecl")) then 60 | -- printf("typedef %s %s", structDecl:name(), cur:name()) 61 | if (cur:name() == typedefName) then 62 | g_structDecl = structDecl 63 | return V.Break 64 | end 65 | end 66 | end 67 | 68 | return V.Continue 69 | end) 70 | 71 | -- Get a line from a source file. 72 | -- XXX: we could coalesce all queries for one file into one io.open() and file 73 | -- traversal, but it seems that parsing is the bottleneck anyway. 74 | local function getline(fn, line) 75 | local f = io.open(fn) 76 | if (f == nil) then 77 | return 78 | end 79 | 80 | local str 81 | while (line > 0) do 82 | str = f:read("*l") 83 | line = line-1 84 | end 85 | 86 | f:close() 87 | return str 88 | end 89 | 90 | local visitor = cl.regCursorVisitor( 91 | function(cur, parent) 92 | if (cur:haskind("MemberRefExpr")) then 93 | local membname = cur:name() 94 | if (membname==memberName) then 95 | local def = cur:definition():parent() 96 | if (def:haskind("StructDecl") and def == g_structDecl) then 97 | local fn, line = cur:location() 98 | local str = table.concat(cur:_tokens(tu), "") 99 | printf("%s:%d: %s", g_curFileName, line, getline(fn,line) or str) 100 | end 101 | end 102 | end 103 | 104 | return V.Recurse 105 | end) 106 | 107 | for fi=1,#files do 108 | local fn = files[fi] 109 | g_curFileName = fn 110 | 111 | local index = cl.createIndex(true, false) 112 | local tu = index:parse(fn, clangOpts) 113 | if (tu == nil) then 114 | errprintf("Failed parsing '%s'", fn) 115 | os.exit(1) 116 | end 117 | 118 | if (not quiet) then 119 | local diags = tu:diagnostics() 120 | for i=1,#diags do 121 | local d = diags[i] 122 | errprintf("%s", d.text) 123 | end 124 | end 125 | 126 | if (not dryrun) then 127 | local tuCursor = tu:cursor() 128 | tuCursor:children(visitorGetType) 129 | 130 | if (g_structDecl == nil) then 131 | if (not quiet) then 132 | errprintf("%s: Didn't find declaration for '%s'", fn, typedefName) 133 | end 134 | else 135 | tuCursor:children(visitor) 136 | g_structDecl = nil 137 | end 138 | end 139 | end 140 | -------------------------------------------------------------------------------- /ljclang/parsecmdline_pk.lua: -------------------------------------------------------------------------------- 1 | 2 | local pairs = pairs 3 | 4 | -- Get options from command line. 5 | -- 6 | -- opts, args = getopts(opt_meta, arg, usage_func) 7 | -- 8 | -- : table { [optletter]=true/false/string/tab } 9 | -- : string sequence 10 | -- 11 | -- : Meta-information about options, table of [optletter]=, 12 | -- false: doesn't have argument (i.e. is switch) 13 | -- true: has argument, collect once 14 | -- 1: has argument, collect all 15 | -- 16 | -- : The arguments provided to the program 17 | -- : Function to print usage and terminate. Should accept optional 18 | -- prefix line. 19 | local function getopts(opt_meta, arg, usage) 20 | local opts = {} 21 | for k,v in pairs(opt_meta) do 22 | -- Init tables for collect-multi options. 23 | if (v and v~=true) then 24 | opts[k] = {} 25 | end 26 | end 27 | 28 | -- The extracted positional arguments: 29 | local args = {} 30 | 31 | local skipnext = false 32 | for i=1,#arg do 33 | if (skipnext) then 34 | skipnext = false 35 | goto next 36 | end 37 | 38 | if (arg[i]:sub(1,1)=="-") then 39 | local opt = arg[i]:sub(2) 40 | skipnext = opt_meta[opt] 41 | if (skipnext == nil) then 42 | usage("Unrecognized option "..arg[i]) 43 | elseif (skipnext) then 44 | if (arg[i+1] == nil) then 45 | usage() 46 | end 47 | if (skipnext~=true) then 48 | opts[opt][#opts[opt]+1] = arg[i+1] 49 | else 50 | opts[opt] = arg[i+1] 51 | end 52 | else 53 | opts[opt] = true 54 | end 55 | else 56 | local ii=1 57 | for j=i,#arg do 58 | args[ii] = arg[j] 59 | ii = ii+1 60 | end 61 | break 62 | end 63 | ::next:: 64 | end 65 | 66 | return opts, args 67 | end 68 | 69 | return { 70 | getopts = getopts 71 | } 72 | -------------------------------------------------------------------------------- /ljclang/test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | 3 | local arg = arg 4 | 5 | local assert = assert 6 | local print = print 7 | local require = require 8 | local tostring = tostring 9 | 10 | local string = require("string") 11 | local os = require("os") 12 | 13 | ---------- 14 | 15 | assert(arg[1], "Usage: "..arg[0].." ...") 16 | 17 | local cl = require("ljclang") 18 | 19 | arg[0] = nil 20 | local tu = cl.createIndex():parse(arg, {"DetailedPreprocessingRecord"}) 21 | 22 | -- NOTE: we don't need to keep the Index_t reference around, test this. 23 | collectgarbage() 24 | 25 | if (tu == nil) then 26 | print('TU is nil') 27 | os.exit(1) 28 | end 29 | 30 | local cur = tu:cursor() 31 | assert(cur==cur) 32 | assert(cur ~= nil) 33 | assert(cur:kindnum() == "CXCursor_TranslationUnit") 34 | assert(cur:haskind("TranslationUnit")) 35 | 36 | print("TU: "..cur:name()..", "..cur:displayName()) 37 | local fn = arg[1]:gsub(".*/","") 38 | print(fn.." in TU: "..tu:file(fn)..", "..tu:file(arg[1])) 39 | 40 | local diags = tu:diagnostics() 41 | for i=1,#diags do 42 | local d = diags[i] 43 | print("diag "..i..": "..d.category..", "..d.text) 44 | end 45 | 46 | local V = cl.ChildVisitResult 47 | 48 | local ourtab = {} 49 | 50 | local visitor = cl.regCursorVisitor( 51 | function(cur, parent) 52 | ourtab[#ourtab+1] = cl.Cursor(cur) 53 | 54 | if (cur:haskind("EnumConstantDecl")) then 55 | print(string.format("%s: %d", cur:name(), cur:enumval())) 56 | end 57 | 58 | local isdef = (cur:haskind("FunctionDecl")) and cur:isDefinition() 59 | 60 | -- print(string.format("[%3d] %50s <- %s", tonumber(cur:kindnum()), tostring(cur), tostring(parent))) 61 | print(string.format("%3d [%12s%s] %50s <- %s", #ourtab, cur:kind(), 62 | isdef and " (def)" or "", tostring(cur), tostring(parent))) 63 | 64 | if (cur:haskind("CXXMethod")) then 65 | print("("..cur:access()..")") 66 | end 67 | 68 | return V.Continue 69 | end) 70 | 71 | cur:children(visitor) 72 | 73 | local tab = cur:children() 74 | print("TU has "..#tab.." direct descendants:") 75 | for i=1,#tab do 76 | print(i..": "..tab[i]:kind()..": "..tab[i]:displayName()) 77 | assert(tab[i] == ourtab[i]) 78 | end 79 | -------------------------------------------------------------------------------- /locally: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | src_dir=$(readlink -f "$(dirname "$0")") 4 | lua_path=${LUA_PATH:-$(luajit -e 'print(package.path)')} 5 | 6 | LJ_CDEFDB_DIR="$src_dir" LUA_PATH="$src_dir/share/?.lua;$lua_path" "$@" 7 | -------------------------------------------------------------------------------- /share/cdef.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2014-2015 Brian Downing. MIT License. 2 | 3 | local ffi = require 'ffi' 4 | 5 | local cdef, C, ffi_string, floor, min = 6 | ffi.cdef, ffi.C, ffi.string, math.floor, math.min 7 | 8 | local dbg = function () end 9 | -- dbg = print 10 | 11 | local cdefdb_dir = os.getenv('LJ_CDEFDB_DIR') or require('cdefdb.config').dir 12 | local cdefdb_open = require 'cdefdb.open' 13 | 14 | local db = cdefdb_open(cdefdb_dir .. '/cdefdb') 15 | 16 | local db_num_stmts = db.header.num_stmts 17 | local db_num_constants = db.header.num_constants 18 | local db_stmts = db.stmts 19 | local db_stmt_deps = db.stmt_deps 20 | local db_constants_idx = db.constants_idx 21 | local db_kind_name_file_idx = db.kind_name_file_idx 22 | local db_strings = db.strings 23 | 24 | local strcache = setmetatable({ }, { __mode = 'v' }) 25 | local function get_string(offset) 26 | local ret = strcache[offset] 27 | if not ret then 28 | ret = ffi_string(db_strings + offset) 29 | strcache[offset] = ret 30 | end 31 | -- print('get_string', offset, ret) 32 | return ret 33 | end 34 | 35 | local function foreach_dep(offset, fun) 36 | -- print('foreach_dep', offset) 37 | while db_stmt_deps[offset] ~= -1 do 38 | -- print('foreach_dep', offset, db_stmt_deps[offset]) 39 | fun(db_stmt_deps[offset]) 40 | offset = offset + 1 41 | end 42 | end 43 | 44 | local function string_lt(offset, str) 45 | return C.cdefdb_strcmp(db_strings + offset, str) < 0 46 | end 47 | 48 | local function string_ge(offset, str) 49 | return C.cdefdb_strcmp(db_strings + offset, str) >= 0 50 | end 51 | 52 | local function string_eq(offset, str) 53 | return C.cdefdb_strcmp(db_strings + offset, str) == 0 54 | end 55 | 56 | local function lt(a, b) return a < b end 57 | local function gt(a, b) return a > b end 58 | local function ge(a, b) return a > b end 59 | 60 | local function identity(x) return x end 61 | local function constantly(x) 62 | return function () return x end 63 | end 64 | 65 | local function lower_bound(arr, low, high, comp) 66 | local mid 67 | while true do 68 | if low > high then 69 | return low 70 | end 71 | mid = floor((high + low) / 2) 72 | if comp(arr[mid], mid) then -- arr[i] < search 73 | low = mid + 1 74 | else 75 | high = mid - 1 76 | end 77 | end 78 | end 79 | 80 | local function upper_bound(arr, low, high, comp) 81 | local mid 82 | while true do 83 | if low > high then 84 | return high 85 | end 86 | mid = floor((high + low) / 2) 87 | if comp(arr[mid], mid) then -- arr[i] > search 88 | high = mid + 1 89 | else 90 | low = mid - 1 91 | end 92 | end 93 | end 94 | 95 | local function cmp2fn(a, av, b, bv, cmp) 96 | return function (stmt) 97 | if string_eq(stmt[a], av) then 98 | return cmp(stmt[b], bv) 99 | end 100 | return cmp(stmt[a], av) 101 | end 102 | end 103 | 104 | local function string_plus_one(str) 105 | return str:sub(1, -2) .. string.char(str:byte(-1) + 1) 106 | end 107 | 108 | local function find_stmts(kind, name) 109 | local star 110 | if name:sub(-1) == '*' then 111 | name = name:sub(1, -2) 112 | star = true 113 | end 114 | local namf = star and string_plus_one(name) 115 | local cmp_lt_name = cmp2fn('kind', kind, 'name', name, string_lt) 116 | local cmp_ge_namf = 117 | star and cmp2fn('kind', kind, 'name', namf, string_ge) or constantly(false) 118 | local max = db.header.num_stmts 119 | local b = lower_bound( 120 | db_kind_name_file_idx, 121 | 0, db.header.num_stmts, 122 | function (i, mid) 123 | local stmt = db_stmts[i] 124 | if cmp_ge_namf(stmt) then 125 | max = min(mid, max) 126 | end 127 | -- print(name, get_string(stmt.name), namf, mid, max) 128 | return cmp_lt_name(stmt) 129 | end) 130 | if not star then 131 | local i = db_kind_name_file_idx[b] 132 | if get_string(db_stmts[i].kind) == kind and 133 | get_string(db_stmts[i].name) == name 134 | then 135 | return b, b + 1 136 | else 137 | error("cdef: Couldn't find "..kind.." "..name) 138 | end 139 | end 140 | local cmp_lt_namf = cmp2fn('kind', kind, 'name', namf, string_lt) 141 | local t = lower_bound( 142 | db_kind_name_file_idx, 143 | b, max, 144 | function (i) 145 | return cmp_lt_namf(db_stmts[i]) 146 | end) 147 | -- print('b', b, 'max', max, 't', t) 148 | if b >= t then 149 | error("cdef: No matching "..kind.." "..name.."*") 150 | end 151 | return b, t 152 | end 153 | 154 | local function find_constants(name) 155 | local star 156 | if name:sub(-1) == '*' then 157 | name = name:sub(1, -2) 158 | star = true 159 | end 160 | local namf = star and string_plus_one(name) 161 | local max = db_num_constants 162 | local b = lower_bound( 163 | db_constants_idx, 164 | 0, db_num_constants, 165 | function (entry, mid) 166 | if star and string_ge(entry.name, namf) or false then 167 | max = min(mid, max) 168 | end 169 | -- print(name, name, namf, mid, max) 170 | return string_lt(entry.name, name) 171 | -- local entry_name = get_string(entry.name) 172 | -- if star and entry_name >= namf or false then 173 | -- max = min(mid, max) 174 | -- end 175 | -- -- print(name, name, namf, mid, max) 176 | -- return entry_name < name 177 | end) 178 | if not star then 179 | if get_string(db_constants_idx[b].name) == name then 180 | return b, b + 1 181 | else 182 | error("cdef: Couldn't find constant "..name) 183 | end 184 | end 185 | local t = lower_bound( 186 | db_constants_idx, 187 | b, max, 188 | function (entry) return string_lt(entry.name, namf) end) 189 | -- print('b', b, 'max', max, 't', t) 190 | if b >= t then 191 | error("cdef: No matching constants: "..name.."*") 192 | end 193 | return b, t 194 | end 195 | 196 | local visited = ffi.new('char [?]', db_num_stmts) 197 | local anchored_libs = {} 198 | 199 | local keyword_for_kind = { 200 | StructDecl = 'struct', 201 | UnionDecl = 'union', 202 | } 203 | 204 | local function emit(to_dump, ldbg) 205 | ldbg = ldbg or dbg 206 | local macros = { } 207 | local function dump(idx) 208 | local v = visited[idx] 209 | if v > 0 and v ~= 2 then return end 210 | local stmt = db_stmts[idx] 211 | local kind = get_string(stmt.kind) 212 | if v == 2 then 213 | if kind == 'StructDecl' or kind == 'UnionDecl' then 214 | local s = '/* circular */ ' .. 215 | keyword_for_kind[kind] .. ' '..get_string(stmt.name)..';' 216 | ldbg(s) 217 | cdef(s) 218 | visited[idx] = 3 219 | return 220 | else 221 | error('circular '..kind..' '..get_string(stmt.extent)) 222 | end 223 | end 224 | visited[idx] = 2 225 | foreach_dep(stmt.deps, dump) 226 | foreach_dep(stmt.delayed_deps, function (dep) 227 | to_dump[#to_dump + 1] = dep 228 | end) 229 | if kind == 'MacroDefinition' then 230 | macros[#macros + 1] = 231 | string.format('/* macro */ enum { %s =%s };', 232 | get_string(stmt.name), 233 | get_string(stmt.extent)) 234 | else 235 | if kind == 'StubRef' then 236 | local hash = get_string(stmt.name) 237 | local lib = ffi.load(cdefdb_dir .. '/stubs/'..hash..'.so', true) 238 | anchored_libs[#anchored_libs + 1] = lib 239 | end 240 | local s = get_string(stmt.extent)..';' 241 | ldbg(s) 242 | cdef(s) 243 | end 244 | visited[idx] = 1 245 | end 246 | 247 | ldbg("local ffi = require 'ffi'\nffi.cdef[==[") 248 | 249 | local i = 1 250 | while i <= #to_dump do 251 | dump(to_dump[i]) 252 | i = i + 1 253 | end 254 | for i = 1, #macros do 255 | ldbg(macros[i]) 256 | cdef(macros[i]) 257 | end 258 | 259 | ldbg(']==]') 260 | end 261 | 262 | local function to_dump_constants(to_dump, name) 263 | local b, t = find_constants(name) 264 | for i = b, t-1 do 265 | to_dump[#to_dump + 1] = db_constants_idx[i].stmt 266 | -- print('constant', i, to_dump[#to_dump]) 267 | end 268 | end 269 | 270 | local function to_dump_stmts(to_dump, kind, name) 271 | local b, t = find_stmts(kind, name) 272 | for i = b, t-1 do 273 | to_dump[#to_dump + 1] = db_kind_name_file_idx[i] 274 | -- print('stmt', i, to_dump[#to_dump]) 275 | end 276 | end 277 | 278 | local kindmap = { 279 | functions = 'FunctionDecl', 280 | variables = 'VarDecl', 281 | structs = 'StructDecl', 282 | unions = 'UnionDecl', 283 | enums = 'EnumDecl', 284 | typedefs = 'TypedefDecl', 285 | } 286 | 287 | local loaded = { } 288 | local function cdef_(spec) 289 | local to_dump = { } 290 | for k, v in pairs(spec) do 291 | if type(v) == 'string' then 292 | v = { v } 293 | end 294 | if k == 'constants' then 295 | for _, name in ipairs(v) do 296 | if not loaded[name] then 297 | to_dump_constants(to_dump, name) 298 | loaded[name] = true 299 | end 300 | end 301 | elseif kindmap[k] then 302 | for _, name in ipairs(v) do 303 | local kname = k..'\0'..name 304 | if not loaded[kname] then 305 | to_dump_stmts(to_dump, kindmap[k], name) 306 | loaded[kname]= true 307 | end 308 | end 309 | end 310 | end 311 | emit(to_dump, spec.verbose and print) 312 | return C, ffi 313 | end 314 | 315 | return cdef_ 316 | 317 | -- cdef_{ funcs = 'ev_*', constants = 'EV*' } 318 | -- cdef_{ funcs = { 'open', 'close', 'read', 'write' }, constants = 'O_*' } 319 | 320 | -- local to_dump = { i } 321 | -- to_dump_constants(to_dump, 'EV') 322 | -- emit(to_dump) 323 | 324 | -- cdef_{ constants = 'DEFFILEMODE' } 325 | -- cdef_{ constants = 'SQLITE_IOERR_*' } 326 | -- cdef_{ constants = 'EV_READ' } 327 | -- cdef_{ constants = 'EVLOOP_NONBLOCK' } 328 | -- cdef_{ functions = 'ev_*' } 329 | -------------------------------------------------------------------------------- /share/cdefdb/cdefs.lua: -------------------------------------------------------------------------------- 1 | local ffi = require 'ffi' 2 | 3 | ffi.cdef[[ 4 | struct cdefdb_header { 5 | char id[16]; 6 | int32_t priority; 7 | int32_t num_stmts; 8 | int32_t num_constants; 9 | int32_t stmts_offset; 10 | int32_t stmt_deps_offset; 11 | int32_t constants_idx_offset; 12 | int32_t file_kind_name_idx_offset; 13 | int32_t file_name_kind_idx_offset; 14 | int32_t kind_file_name_idx_offset; 15 | int32_t kind_name_file_idx_offset; 16 | int32_t name_file_kind_idx_offset; 17 | int32_t name_kind_file_idx_offset; 18 | int32_t strings_offset; 19 | }; 20 | struct cdefdb_stmts_t { 21 | int32_t name; 22 | int32_t kind; 23 | int32_t extent; 24 | int32_t file; 25 | int32_t deps; 26 | int32_t delayed_deps; 27 | }; 28 | struct cdefdb_constants_idx_t { 29 | int32_t name; 30 | int32_t stmt; 31 | }; 32 | 33 | int cdefdb_strcmp(const char *s1, const char *s2) asm("strcmp"); 34 | int cdefdb_open(const char *pathname, int flags) asm("open"); 35 | void *cdefdb_mmap(void *addr, size_t length, int prot, int flags, 36 | int fd, int64_t offset) asm("mmap64"); 37 | int cdefdb_close(int fd) asm("close"); 38 | 39 | enum { 40 | CDEFDB_O_RDONLY = 0, 41 | CDEFDB_PROT_READ = 1, 42 | CDEFDB_MAP_SHARED = 1, 43 | }; 44 | ]] 45 | -------------------------------------------------------------------------------- /share/cdefdb/open.lua: -------------------------------------------------------------------------------- 1 | require 'cdefdb.cdefs' 2 | 3 | local ffi = require 'ffi' 4 | local C = ffi.C 5 | 6 | local function cdefdb_open(filename, size) 7 | local db = { } 8 | 9 | if not size then 10 | local fh = assert(io.open(filename, 'r')) 11 | size = fh:seek('end') 12 | fh:close() 13 | end 14 | 15 | local fd = C.cdefdb_open(filename, 16 | C.CDEFDB_O_RDONLY) 17 | assert(fd >= 0) 18 | local m = C.cdefdb_mmap(nil, size, 19 | C.CDEFDB_PROT_READ, 20 | C.CDEFDB_MAP_SHARED, 21 | fd, 0) 22 | assert(m ~= ffi.cast('void *', -1), ffi.errno()) 23 | C.cdefdb_close(fd) 24 | 25 | db.map_base = ffi.cast('char *', m) 26 | db.size = size 27 | db.header = ffi.cast('struct cdefdb_header *', db.map_base) 28 | local id = ffi.string(db.header.id, 16) 29 | if id ~= 'cdefdb 1.0.0\0\0\0\0' then 30 | error('bad cdefdb file '..filename) 31 | end 32 | 33 | local function db_add(name, ctype) 34 | db[name] = ffi.cast(ctype, db.map_base + db.header[name..'_offset']) 35 | end 36 | db_add('stmts', 'struct cdefdb_stmts_t *') 37 | db_add('stmt_deps', 'int32_t *') 38 | db_add('constants_idx', 'struct cdefdb_constants_idx_t *') 39 | db_add('file_kind_name_idx', 'int32_t *') 40 | db_add('file_name_kind_idx', 'int32_t *') 41 | db_add('kind_file_name_idx', 'int32_t *') 42 | db_add('kind_name_file_idx', 'int32_t *') 43 | db_add('name_file_kind_idx', 'int32_t *') 44 | db_add('name_kind_file_idx', 'int32_t *') 45 | db_add('strings', 'char *') 46 | 47 | return db 48 | end 49 | 50 | return cdefdb_open 51 | -------------------------------------------------------------------------------- /share/cdefdb/write.lua: -------------------------------------------------------------------------------- 1 | require 'cdefdb.cdefs' 2 | 3 | local ffi = require 'ffi' 4 | 5 | local function cdefdb_write(fh, stmts, constants, priority) 6 | local stmt_i = { } 7 | for _, stmt in pairs(stmts) do 8 | stmt_i[stmt.idx] = stmt 9 | end 10 | local constants_i = { } 11 | for c, stmt in pairs(constants) do 12 | table.insert(constants_i, { 13 | name = c, 14 | stmt = stmt 15 | }) 16 | end 17 | 18 | local dns = { -1 } 19 | local dns_i = 1 20 | local dnmap = { ['-1'] = 0 } 21 | local function intern_dn(dn) 22 | local key = table.concat(dn, ',') 23 | if not dnmap[key] then 24 | for i = 1, #dn do 25 | dns[#dns + 1] = dn[i] 26 | end 27 | dnmap[key] = dns_i 28 | dns_i = dns_i + #dn 29 | end 30 | return dnmap[key] 31 | end 32 | local strings = { } 33 | local strings_n = { } 34 | local strings_i = 0 35 | local stringmap = { } 36 | local function intern_string(str) 37 | str = str 38 | if not stringmap[str] then 39 | strings[#strings + 1] = str 40 | stringmap[str] = strings_i 41 | strings_n[#strings_n + 1] = strings_i 42 | strings_i = strings_i + #str + 1 43 | end 44 | return stringmap[str] 45 | end 46 | 47 | table.sort(constants_i, function (a, b) return a.name < b.name end) 48 | for _, c in ipairs(constants_i) do 49 | -- so it's sorted/consistent 50 | c.name_i = intern_string(c.name) 51 | end 52 | 53 | local buf = { } 54 | buf.header = ffi.new('struct cdefdb_header') 55 | ffi.copy(buf.header.id, 'cdefdb 1.0.0') 56 | buf.header.priority = priority or 0 57 | buf.header.num_stmts = #stmt_i 58 | buf.stmts = ffi.new('struct cdefdb_stmts_t [?]', #stmt_i) 59 | for i, stmt in ipairs(stmt_i) do 60 | buf.stmts[i-1].name = intern_string(stmt.name) 61 | buf.stmts[i-1].kind = intern_string(stmt.kind) 62 | buf.stmts[i-1].extent = intern_string(stmt.extent) 63 | buf.stmts[i-1].file = intern_string(stmt.file) 64 | 65 | for _, dep_type in ipairs{'deps', 'delayed_deps'} do 66 | local deps = { } 67 | for tag, _ in pairs(stmt[dep_type]) do 68 | if stmts[tag] then 69 | deps[#deps + 1] = stmts[tag].idx - 1 70 | end 71 | end 72 | table.sort(deps) 73 | deps[#deps + 1] = -1 74 | buf.stmts[i-1][dep_type] = intern_dn(deps) 75 | end 76 | end 77 | 78 | function make_int32_array(t, key) 79 | key = key or function (e) return e end 80 | local buf = ffi.new('int32_t [?]', #t) 81 | for i = 1, #t do 82 | buf[i-1] = key(t[i]) 83 | end 84 | return buf 85 | end 86 | buf.stmt_deps = make_int32_array(dns) 87 | 88 | buf.header.num_constants = #constants_i 89 | buf.constants_idx = ffi.new('struct cdefdb_constants_idx_t [?]', #constants_i) 90 | for i, c in ipairs(constants_i) do 91 | buf.constants_idx[i-1].name = c.name_i 92 | buf.constants_idx[i-1].stmt = c.stmt.idx - 1 93 | end 94 | 95 | local function sort3keys(a, b, c) 96 | return function (x, y) 97 | if x[a] == y[a] then 98 | if x[b] == y[b] then 99 | return x[c] < y[c] 100 | end 101 | return x[b] < y[b] 102 | end 103 | return x[a] < y[a] 104 | end 105 | end 106 | local function make_stmt_idx(a, b, c) 107 | table.sort(stmt_i, sort3keys(a, b, c)) 108 | return make_int32_array(stmt_i, 109 | function (stmt) return stmt.idx-1 end) 110 | end 111 | buf.file_kind_name_idx = make_stmt_idx('file', 'kind', 'name') 112 | buf.file_name_kind_idx = make_stmt_idx('file', 'name', 'kind') 113 | buf.kind_file_name_idx = make_stmt_idx('kind', 'file', 'name') 114 | buf.kind_name_file_idx = make_stmt_idx('kind', 'name', 'file') 115 | buf.name_file_kind_idx = make_stmt_idx('name', 'file', 'kind') 116 | buf.name_kind_file_idx = make_stmt_idx('name', 'kind', 'file') 117 | 118 | buf.strings = ffi.new('char [?]', strings_i) 119 | local slen = 0 120 | for i, str in ipairs(strings) do 121 | ffi.copy(buf.strings + slen, ffi.cast('char *', str), #str + 1) 122 | slen = slen + #str + 1 123 | end 124 | 125 | local o = ffi.sizeof(buf.header) 126 | local function header_offset(name) 127 | buf.header[name..'_offset'] = o 128 | o = o + ffi.sizeof(buf[name]) 129 | end 130 | header_offset('stmts') 131 | header_offset('stmt_deps') 132 | header_offset('constants_idx') 133 | header_offset('file_kind_name_idx') 134 | header_offset('file_name_kind_idx') 135 | header_offset('kind_file_name_idx') 136 | header_offset('kind_name_file_idx') 137 | header_offset('name_file_kind_idx') 138 | header_offset('name_kind_file_idx') 139 | header_offset('strings') 140 | 141 | local function emit(o, len) 142 | assert(fh:write(ffi.string(o, len or ffi.sizeof(o)))) 143 | end 144 | 145 | emit(buf.header) 146 | emit(buf.stmts) 147 | emit(buf.stmt_deps) 148 | emit(buf.constants_idx) 149 | emit(buf.file_kind_name_idx) 150 | emit(buf.file_name_kind_idx) 151 | emit(buf.kind_file_name_idx) 152 | emit(buf.kind_name_file_idx) 153 | emit(buf.name_file_kind_idx) 154 | emit(buf.name_kind_file_idx) 155 | emit(buf.strings) 156 | end 157 | 158 | return cdefdb_write 159 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env luajit 2 | 3 | local arg = arg 4 | 5 | local assert = assert 6 | local print = print 7 | local require = require 8 | local tostring = tostring 9 | 10 | local string = require("string") 11 | local os = require("os") 12 | 13 | ---------- 14 | 15 | assert(arg[1], "Usage: "..arg[0].." ...") 16 | 17 | local cl = require("ljclang") 18 | 19 | arg[0] = nil 20 | local tu = cl.createIndex():parse(arg, {"DetailedPreprocessingRecord"}) 21 | 22 | -- NOTE: we don't need to keep the Index_t reference around, test this. 23 | collectgarbage() 24 | 25 | if (tu == nil) then 26 | print('TU is nil') 27 | os.exit(1) 28 | end 29 | 30 | local cur = tu:cursor() 31 | assert(cur==cur) 32 | assert(cur ~= nil) 33 | assert(cur:kindnum() == "CXCursor_TranslationUnit") 34 | assert(cur:haskind("TranslationUnit")) 35 | 36 | -- print("TU: "..cur:name()..", "..cur:displayName()) 37 | -- local fn = arg[1]:gsub(".*/","") 38 | -- print(fn.." in TU: "..tu:file(fn)..", "..tu:file(arg[1])) 39 | 40 | -- local diags = tu:diagnostics() 41 | -- for i=1,#diags do 42 | -- local d = diags[i] 43 | -- print("diag "..i..": "..d.category..", "..d.text) 44 | -- end 45 | 46 | -- local V = cl.ChildVisitResult 47 | 48 | -- local ourtab = {} 49 | 50 | -- local visitor = cl.regCursorVisitor( 51 | -- function(cur, parent) 52 | -- ourtab[#ourtab+1] = cl.Cursor(cur) 53 | 54 | -- if (cur:haskind("EnumConstantDecl")) then 55 | -- print(string.format("%s: %d", cur:name(), cur:enumval())) 56 | -- end 57 | 58 | -- local isdef = (cur:haskind("FunctionDecl")) and cur:isDefinition() 59 | 60 | -- -- print(string.format("[%3d] %50s <- %s", tonumber(cur:kindnum()), tostring(cur), tostring(parent))) 61 | -- print(string.format("%3d [%12s%s] %50s <- %s %s", #ourtab, cur:kind(), 62 | -- isdef and " (def)" or "", tostring(cur), tostring(parent), tostring(cur:type()))) 63 | 64 | -- if (cur:haskind("CXXMethod")) then 65 | -- print("("..cur:access()..")") 66 | -- end 67 | 68 | -- return V.Continue 69 | -- end) 70 | 71 | -- cur:children(visitor) 72 | 73 | function recurse(cur, indent, visited) 74 | if cur:haskind("MacroExpansion") then 75 | return 76 | end 77 | indent = indent or '' 78 | local f, b, e = cur:location('offset') 79 | local tag = string.format('%s:%d:%d', f or '?', b or 0, e or 0) 80 | if visited then 81 | if visited[tag] then 82 | print(indent..'CYCLE') 83 | return 84 | end 85 | visited[tag] = true 86 | end 87 | local isdef = (cur:haskind("FunctionDecl")) and cur:isDefinition() 88 | local file, row, col, erow, ecol = cur:presumedLocation() 89 | local type = cur:type() 90 | print(string.format("%s[%12s%s] %50s <- %s <%s> (%s::%d:%d::%d:%d - %s)", indent, cur:kind(), 91 | isdef and " (def)" or "", tostring(cur), tostring(type), type and tonumber(type:kindnum()), 92 | file or '?', row or 0, col or 0, erow or 0, ecol or 0, tag)) 93 | if cur:haskind("TypedefDecl") then 94 | local tdtype = cur:typedefType() 95 | local name = tdtype:name() 96 | local ptr, arr = name:match('^[^*]*([^[]*)(%[.*)') 97 | local pre, post = '', '' 98 | while true do 99 | print(string.format("%sTypedef type: %s <%s>", indent..' ', tdtype, tonumber(tdtype:kindnum()))) 100 | print('postfix', ptr, arr) 101 | if tdtype:haskind('ConstantArray') then 102 | post = post .. '['..tdtype:arraySize()..']' 103 | tdtype = tdtype:arrayElementType() 104 | elseif tdtype:haskind('Pointer') then 105 | local ptr = '*' 106 | if tdtype:isConstQualified() then 107 | ptr = ptr .. 'const ' 108 | end 109 | pre = ptr .. pre 110 | tdtype = tdtype:pointee() 111 | else 112 | break 113 | end 114 | end 115 | local typedecl = tdtype:declaration() 116 | if typedecl:haskind('StructDecl') then 117 | if typedecl:name() ~= '' then 118 | print('typedef struct '..typedecl:name()..' '..(ptr or '')..cur:name()..(arr or '')..';') 119 | end 120 | end 121 | if not visited then 122 | recurse(tdtype:declaration(), indent .. ' TYPEDEFTYPE-> ', { [tag] = true }) 123 | end 124 | end 125 | if cur:haskind("MacroDefinition") then 126 | local indent = indent..' ' 127 | local toks = cur:_tokens() 128 | if toks then 129 | for k, v in pairs(toks) do 130 | print(string.format("%s%s: %s", indent, k, v)) 131 | end 132 | end 133 | end 134 | if cur:haskind("TypeRef") and not visited then 135 | local visited = { [tag] = true } 136 | recurse(cur:type():declaration(), indent .. ' TYPE-> ', visited) 137 | end 138 | local kids = cur:children() 139 | for i, k in ipairs(kids) do 140 | recurse(k, indent .. ' ', visited) 141 | end 142 | end 143 | 144 | recurse(cur) 145 | --------------------------------------------------------------------------------