├── .gitignore ├── design-old.txt ├── design.txt ├── lbind ├── config.lua ├── gen │ └── lua.lua ├── init.lua ├── strip.lua ├── typeinfo.lua ├── types.lua └── utils.lua ├── runtime ├── lbind.c └── lbind.h ├── test └── gd.lbind.lua └── tt.lua /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | bench.c 3 | test.lua 4 | test.c 5 | tags* 6 | */tags* 7 | *.css 8 | *.dll 9 | *.exe 10 | *.html 11 | *~ 12 | -------------------------------------------------------------------------------- /design-old.txt: -------------------------------------------------------------------------------- 1 | lbind 0.2 设计 2 | ============== 3 | 4 | 通过参考 [LuaNativeObject],我们改变了 `lbind` 的设计。采用了类似 5 | LuaNativeObject 的设计方法,即: 6 | 7 | - 表格代表可嵌套集合 8 | - 冒号方法代表选项 9 | - 内部用表格而不是 `upvalue` 整体描述数据,数据透明地放置在导出表 10 | 中,导出表由 `exports` 函数指定。该函数同时将表指定为导出表,并 11 | 为导出表设置元表使之能访问 `lbind` 内的函数。 12 | - 导出数据透明: 13 | - `tag` 域指明数据类型(`module`,`class`,`enum`,`variable` 14 | ,`func` 等等) 15 | - 其他 `hash` 域指明必要的选项。 16 | - 数组域指明包含的项目。 17 | - 导出数据不包含任何创建的操作。而由 `gen` 模块单独提供针对 `tag` 18 | 的函数作为导出的方法。也就是说,`gen` + 导出表就可以生成绑定文本 19 | 了。 20 | - 类型系统重新设计,骨架上改成上述的内容,具体生产方法不变。仍然是 21 | 采用模板式生成方式。 22 | - `gen_xxx` 模块被分解为两个部分: `api` 模块和 `gen` 模块,前者提 23 | 供生成数据表格(称 `ast` 也可)的接口,而后者提供用于从数据表格 24 | 中产生实际的绑定代码的函数体。 25 | - `driver` 模块提供了将两者结合在一起的驱动部分。 26 | - `init` 提供了 `exports`,`gen` 等函数。 27 | 28 | [LuaNativeObject]: http://github.com/Neopallium/LuaNativeObject 29 | 30 | 语法 31 | ---- 32 | 33 | variable, func 和 enum 作为原子类型。即:只有方法,不接受表格作为参数 34 | 。 35 | 36 | var_obj = variable(name) 37 | 38 | variable 接受字符串作为变量名,返回一个表格作为对象本身。 39 | 40 | var_obj:readonly() 41 | var_obj:writeonly() 42 | 43 | 设置只读/只写属性。这将决定了这个变量在其模块母表中的只读/只写属性。注 44 | 意:目前这个可能实现不了= = 45 | 46 | var_obj:type(type_obj) 47 | 48 | 设置变量的类型。 49 | 50 | func_obj = func(name) 51 | 52 | func 函数接受字符串作为函数在 Lua 中的名字。 53 | 54 | func_obj:alias(name) 55 | 56 | 函数别名 57 | 58 | func_obj:hide(name or typelist...) 59 | 60 | 隐藏函数别名,或者特定重载。如果第一个参数是字符串则隐藏对应的别名,否 61 | 则隐藏对应的重载。 62 | 63 | func_obj:args(typelist...) 64 | 65 | 开启一个新的重载,函数下面的操作 **必须** 在重载的基础上才能操作。如果 66 | 没有重载,则默认的重载是“无参数”重载,即函数不接受任何参数。 67 | 68 | 如果参数签名和之前的某个重载完全一致(除参数名和默认值以外),则那个重 69 | 载会被选中用于修改。 70 | 71 | 除非是选中了某个重载(即该重载在 C++ 的意义上会造成歧义),否则该重载 72 | 的 Lua 类型不允许和之前的任何重载相同。这一点会在 gen 的时候进行检查, 73 | 如果满足这个要求,会在 gen 的时候警告。 74 | 75 | func_obj:rets(typelist...) 76 | 77 | 设置当前重载的返回值。如果返回值中有名字和函数重载中的名字相同,则其类 78 | 型必须相同,否则 gen 时警告。 79 | 80 | func_obj:prev(text) 81 | func_obj:post(text) 82 | func_obj:call(text) 83 | func_obj:body(text) 84 | 85 | 设置代码: 86 | - prev 指定的文本会出现在当前重载在提取参数之后,调用具体函数之前; 87 | - post 会出现在调用函数之后,产生返回值之前; 88 | - body 如果提供,则忽略其余的三个,会替代 prev+call+post 的部分; 89 | - call 会替代用于调用函数的代码; 90 | 91 | 理论上,可以通过以下代码凭空产生一个 Lua 函数: 92 | 93 | func "test" 94 | :args(lua_State "L", lua_Object "obj", int "a", int "b", int "c") 95 | :rets(int "d", int "e") 96 | :body [[ 97 | printf("lua_State = %p, a = %d, b = %d, c = %d\n", L, a, b, c); 98 | lua_call(obj, 0, 0); 99 | d = 10; 100 | e = lua_tonumber(L, -1); 101 | ]] 102 | 103 | 这段“绑定”代码会产生这样的 C 函数: 104 | 105 | int binder_test(lua_State *L) { 106 | int obj = 1; 107 | int a = lua_tonumber(L, 2); 108 | int b = lua_tonumber(L, 3); 109 | int c = lua_tonumber(L, 4); 110 | int d; 111 | int e; 112 | printf("lua_State = %p, a = %d, b = %d, c = %d\n", L, a, b, c); 113 | lua_call(obj, 0, 0); 114 | d = 10; 115 | e = lua_tonumber(L, -1); 116 | lua_pushinteger(L, d); 117 | lua_pushinteger(L, e); 118 | return 2; 119 | } 120 | 121 | 注意这个函数没有绑定任何其他的 C 函数,它就是作为本身使用的,这个特性 122 | 允许你通过 lbind 实际上书写任何独立的 C 模块。 123 | 124 | -- cc: cc='lunamark' 125 | -------------------------------------------------------------------------------- /design.txt: -------------------------------------------------------------------------------- 1 | lbind 语法设计 2 | 3 | 4 | 原理: 5 | - 方便解析 6 | - 书写方便 7 | - 便于自动生成 8 | 9 | 规则: 10 | - 采用命令式的方式 11 | - 命令是行相关,除非特殊情况,否则命令需要在一行之内写完,否则: 12 | - 行尾采用 \ 续行 13 | - 该行有未完成的 () [] {} 以及 [[]] 14 | 的情况下,可以认为下一行也是当前命令的一部分。 15 | 16 | 如果命令有需要嵌入的其他代码,采用Lua长字符串的方式书写,即 [[ ... ]] 17 | 任何导入到Lua中的名字,都可以通过:的方式指定其在目标代码里面 18 | 的名字。 19 | 20 | 命令语法: 21 | - 直接出现的单词为关键字 22 | - 用尖括号 <...> 包起来的部分是对该处出现的内容的说明 23 | - 用反括号 `...` 包起来的部分是需要原样出现在命令中的字符 24 | - 用方括号 [ ... ] 包含起来的部分是可选内容 25 | - 用大括号 { ... } 包含起来的部分是可以重复多次的可选内容 26 | - 用双方括号 [[ ... ]] 包含起来的部分是引用其他语言的部分,需要采用 Lua 长 27 | 字符串的方式进行书写。 28 | 29 | 基本命令: 30 | submodule `"` `"` 31 | 包含其他的 lbind 文件 32 | 33 | include [[ ]] 34 | 在当前位置嵌入一段目标代码,通常是C/C++代码 35 | 36 | lua [ [ `:` ] ] [[ ]] 37 | 在当前位置嵌入一段Lua的代码,并包装成C函数的形式 38 | 如果没有指定名字,则该代码会在模块加载时即执行。 39 | 40 | eval [[ ]] 41 | 在当前位置直接执行Lua代码,参数为当前的解析器 42 | 43 | export name [ `:` ] [`{` `}`] [ [[ ]] ] 44 | 在当前位置产生一个导出函数。导出函数是以 LUALIB_API 声明的、以 luaopen_ 开头的 45 | 函数。该函数的名字由 name 指定,其中 "." 被替换为 "_"。如果导出函数需要直接导 46 | 出一个类型,则通过名字后面可选的冒号指定(只能指定一个)。后面可选的大括号给出 47 | 了额外可以被导出的内容。可以被导出的内容包括常量、函数、类型、枚举等等。 48 | 另外,可以通过其他的灵活方式进行导出:如果顶层 func 或者 type 前面有 export, 49 | 则该函数或者类型会被加入到导出表。注意,无法通过这种方式指定直接被导出的类型。 50 | 导出命令后可以跟随可选的C代码,该C代码将完全替换自动产生的导出代码,但是嵌入的 51 | C代码中出现的 {{export}} 字样会被替换为导出代码的内容。其他可选的替代名称为: 52 | - {{init}} 替代为导出代码中的声明部分 53 | - {{code}} 替代为导出代码中不包含return的代码部分 54 | - {{nret}} 替换为return返回的数值 55 | 即,原始的导出代码({{export}})可以表示为: {{init}} {{code}} return {{nret}}; 56 | 之后所有会出现嵌入C代码的时候都支持这样的特性。(注意,导出到Lua的C函数的返回 57 | 值始终为int)。 58 | 59 | [ export ] func name [ `:` ] [`(` [] `)`] 60 | [ `->` `(` `)` ] [ [[ ]] ] 61 | 导出函数。该命令可同时用来导出函数,或者为函数做修饰。注意,任何相同导出名的函 62 | 数都被认为是重载函数,会最终产生一个导出函数体(当然,实现函数可以有很多个)。 63 | 该导出函数体也可以通过下面的override命令指定。 64 | 如果给定export关键字,则该函数自动被导出到本文件中出现的第一个export指定的导出 65 | 函数中。如果没有这样的导出函数,则自动以本lbind文件的文件名产生一个默认的 66 | export导出函数,而该函数也会通过此函数导出。 67 | 之所以不是默认导出,是因为函数默认不导出也是很有用的:你可以将生成的源代码包含 68 | 入其他的手写的项目中使用,而不是产生一个独立的Lua C DLL库。 69 | 如果不指定,则被视作该函数所导出的C名称。 70 | 函数参数和返回值列表都是可选的,注意如果返回值只有一个并且 71 | 是匿名返回值(不指定在导出代码中的名字),则可以省略返回值列表的圆括号。只要有 72 | 返回值,则 `->` 符号不可省略。返回值的数量决定了导出函数返回的值(return的值) 73 | 。 74 | 如果返回值是特殊的 `self` 类型,则自动返回参数列表的第一个值。 75 | 可以嵌入C代码,除了在嵌入的C代码中支持{{export}}, {{init}}, {{code}}和{{nret}} 76 | 以外,还支持{{push}}代码,该代码用于将C值推入Lua栈。 77 | 没有参数列表、返回值列表和嵌入C代码的func命令还有一个特殊的功能:当其出现在 78 | type、table、export命令的spec部分中时,并不会产生一个导出函数,而是仅仅将 79 | 或者指代的C函数添加到该命令的导出表中。 80 | 81 | [ export ] field [in|out] name [ `:` ] [ [[ ]] ] 82 | 导出域。域可以通过 `.` 语法和 `[]` 语法在 Lua 中访问。使得产生的类型如同 Lua 83 | 的原生表一样。in|out指定了该函数的处理类型,如果不指定则会同时处理读写操作。 84 | 是该返回值的类型,可选的嵌入C代码规则和func命令相同。 85 | 86 | [ export ] const name value 87 | 指定常量。该常量会被加入到导出表中去。 88 | 89 | [ export ] type name [ `:` ] [ `(` `)` ] [ `{` `}` ] 90 | 导出类型。其实就是导出一系列的函数。加上一个“类型导出函数”,该类型导出函数的 91 | 调用会导致Lua的注册表中多了一个对应类型的元表。需要导出的内容在中指 92 | 定。可以是任何东西。只是这些东西都会被导出到该类型的元表中。 93 | 如果不指定,则意味着该类型的导出函数已经在其他地方定义,因此该类型 94 | 会被类型系统记住而用于之后的导出代码中,但并不会产生任何导出代码。 95 | 96 | [ export ] table name '{' '}' 97 | 和type命令类似,但是导出函数仅仅是返回一个表,并不将其注册到注册表中。 98 | 99 | prefix `"` `"` 100 | 只能出现在中,一旦出现,则指定其中(没有显式指定别名的)C函数 101 | 的名字的前缀。比如,如果指定前缀为 "test_",则其中的func sum会绑定到C中 102 | "test_sum" 函数上。 103 | 104 | style `"` snake|camel `"` 105 | 同样只能出现在中,如果指定了camel或者snake,则函数的命令规则 106 | 也会做对应的修改。比如如果指定了 camel,则上面的函数将会对应 testSum 函数。 107 | 108 | 109 | vim: nu et sw=2 sts=2 fdc=2 fdm=indent 110 | -------------------------------------------------------------------------------- /lbind/config.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local indent = 4 3 | 4 | function M.indent() 5 | return indent 6 | end 7 | 8 | function M.parse_config(config) 9 | end 10 | 11 | return M 12 | -------------------------------------------------------------------------------- /lbind/gen/lua.lua: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/starwing/lbind/dad84cf9bc93f6b6f31ea21cb189268abd7ff1c2/lbind/gen/lua.lua -------------------------------------------------------------------------------- /lbind/init.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ";../?.lua" 2 | local utils = require 'lbind.utils' 3 | local M = {} 4 | 5 | local function body(name, tag) 6 | return function(body) 7 | body.name = name 8 | body.tag = tag 9 | return body 10 | end 11 | end 12 | 13 | local function code(string, tag) 14 | local t = utils.getlines(string) 15 | t.tag = tag or "code" 16 | return t 17 | end 18 | 19 | local function codemethod(name) 20 | return function(self, string) 21 | local t = code(string, name) 22 | local last = #self 23 | if self[last] then 24 | self[last][name] = t 25 | end 26 | return self 27 | end 28 | end 29 | 30 | local function stringmethod(name) 31 | return function(self, value) 32 | self[name] = utils.trim(value) 33 | return self 34 | end 35 | end 36 | 37 | local function vamethod(name, new) 38 | return function(self, ...) 39 | local t = {...} 40 | local cur 41 | local last = #self 42 | if new or last == 0 then 43 | cur = {} 44 | self[last+1] = cur 45 | else 46 | cur = self[last] 47 | end 48 | if cur then 49 | cur[name] = t 50 | end 51 | return self 52 | end 53 | end 54 | 55 | local function aliasmethod(self, name) 56 | local names = self.names 57 | if not names then 58 | names = {} 59 | self.names = names 60 | end 61 | names[#names+1] = name 62 | return self 63 | end 64 | 65 | local funcMT = { 66 | alias = aliasmethod, 67 | args = vamethod('args', 'new'), 68 | rets = vamethod 'rets', 69 | body = codemethod 'body', 70 | call = codemethod 'call', 71 | post = codemethod 'post', 72 | prev = codemethod 'prev', 73 | cname = stringmethod 'cname', 74 | lname = stringmethod 'lname', 75 | } 76 | funcMT.__index = funcMT 77 | funcMT.__call = funcMT.args 78 | 79 | local function func(name, tag) 80 | return setmetatable({ 81 | name = name, 82 | tag = tag or "func", 83 | }, funcMT) 84 | end 85 | 86 | function M.export(t) 87 | for k, v in pairs(M) do 88 | t[k] = v 89 | end 90 | return M 91 | end 92 | 93 | function M.module(name) 94 | return body(name, 'module') 95 | end 96 | 97 | function M.subfiles(list, ...) 98 | if type(list) ~= 'table' then 99 | list = {list, ...} 100 | end 101 | list.tag = "subfiles" 102 | return list 103 | end 104 | 105 | function M.include(file) 106 | return { 107 | tag = 'code'; 108 | "#include <"..file..">"; 109 | } 110 | end 111 | 112 | function M.include_local(file) 113 | return { 114 | tag = 'code'; 115 | "#include \""..file.."\""; 116 | } 117 | end 118 | 119 | function M.code(string) 120 | return code(string, "code") 121 | end 122 | 123 | function M.lua(string) 124 | return code(string, "lua") 125 | end 126 | 127 | function M.object(name) 128 | return body(name, "object") 129 | end 130 | 131 | function M.method(name) 132 | return func(name, "method") 133 | end 134 | 135 | function M.func(name) 136 | return func(name) 137 | end 138 | 139 | return M 140 | -------------------------------------------------------------------------------- /lbind/strip.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | 3 | = ABOUT 4 | This module uses Roberto Ierusalimschy's powerful new pattern matching library 5 | LPeg[1] to tokenize Lua source-code in to a table of tokens. I think it handles 6 | all of Lua's syntax, but if you find anything missing I would appreciate a mail 7 | at peter@peterodding.com. This lexer is based on the BNF[2] from the Lua manual. 8 | 9 | = USAGE 10 | I've saved my copy of this module under [$LUA_PATH/lexers/lua.lua] which means 11 | I can use it like in the following interactive prompt: 12 | 13 | Lua 5.1.1 Copyright (C) 1994-2006 Lua.org, PUC-Rio 14 | > require 'lexers.lua' 15 | > tokens = lexers.lua [=[ 16 | >> 42 or 0 17 | >> -- some Lua source-code in a string]=] 18 | > = tokens 19 | table: 00422E40 20 | > lexers.lua.print(tokens) 21 | line 1, number: `42` 22 | line 1, whitespace: ` ` 23 | line 1, keyword: `or` 24 | line 1, whitespace: ` ` 25 | line 1, number: `0` 26 | line 1, whitespace: ` 27 | ` 28 | line 2, comment: `-- some Lua source-code in a string` 29 | total of 7 tokens, 2 lines 30 | 31 | The returned table [tokens] looks like this: 32 | 33 | { 34 | -- type , text, line 35 | { 'number' , '42', 1 }, 36 | { 'whitespace', ' ' , 1 }, 37 | { 'keyword' , 'or', 1 }, 38 | { 'whitespace', ' ' , 1 }, 39 | { 'number' , '0' , 1 }, 40 | { 'whitespace', '\n', 1 }, 41 | { 'comment' , '-- some Lua source-code in a string', 2 }, 42 | } 43 | 44 | = CREDITS 45 | Written by Peter Odding, 2007/04/04 46 | 47 | = THANKS TO 48 | - the Lua authors for a wonderful language; 49 | - Roberto for LPeg; 50 | - caffeine for keeping me awake :) 51 | 52 | = LICENSE 53 | Shamelessly ripped from the SQLite[3] project: 54 | 55 | The author disclaims copyright to this source code. In place of a legal 56 | notice, here is a blessing: 57 | 58 | May you do good and not evil. 59 | May you find forgiveness for yourself and forgive others. 60 | May you share freely, never taking more than you give. 61 | 62 | [1] http://www.inf.puc-rio.br/~roberto/lpeg.html 63 | [2] http://lua.org/manual/5.1/manual.html#8 64 | [3] http://sqlite.org 65 | 66 | --]] 67 | 68 | -- since this module is intended to be loaded with require() we receive the 69 | -- name used to load us in ... and pass it on to module() 70 | 71 | -- written for LPeg .5, by the way 72 | local lpeg = require 'lpeg' 73 | local P, R, S, C, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Ct 74 | 75 | -- create a pattern which captures the lua value [id] and the input matching 76 | -- [patt] in a table 77 | local function token(id, patt) return Ct(Cc(id) * C(patt)) end 78 | 79 | local digit = R('09') 80 | 81 | -- range of valid characters after first character of identifier 82 | local idsafe = R('AZ', 'az', '\127\255') + P '_' 83 | 84 | -- operators 85 | local operator = token('operator', P '==' + P '~=' + P '<=' + P '>=' + P '...' 86 | + P '..' + S '+-*/%^#=<>;:,.{}[]()') 87 | -- identifiers 88 | local ident = token('identifier', idsafe * (idsafe + digit + P '.') ^ 0) 89 | 90 | -- keywords 91 | local keyword = token('keyword', (P 'and' + P 'break' + P 'do' + P 'else' + 92 | P 'elseif' + P 'end' + P 'false' + P 'for' + P 'function' + P 'if' + 93 | P 'in' + P 'local' + P 'nil' + P 'not' + P 'or' + P 'repeat' + P 'return' + 94 | P 'then' + P 'true' + P 'until' + P 'while') * -(idsafe + digit)) 95 | 96 | -- numbers 97 | local number_sign = S'+-'^-1 98 | local number_decimal = digit ^ 1 99 | local number_hexadecimal = P '0' * S 'xX' * R('09', 'AF', 'af') ^ 1 100 | local number_float = (digit^1 * P'.' * digit^0 + P'.' * digit^1) * 101 | (S'eE' * number_sign * digit^1)^-1 102 | local number = token('number', number_hexadecimal + 103 | number_float + 104 | number_decimal) 105 | 106 | -- callback for [=[ long strings ]=] 107 | -- ps. LPeg is for Lua what regex is for Perl, which makes me smile :) 108 | local longstring = #(P '[[' + (P '[' * P '=' ^ 0 * P '[')) 109 | local longstring = longstring * P(function(input, index) 110 | local level = input:match('^%[(=*)%[', index) 111 | if level then 112 | local _, stop = input:find(']' .. level .. ']', index, true) 113 | if stop then return stop + 1 end 114 | end 115 | end) 116 | 117 | -- strings 118 | local singlequoted_string = P "'" * ((1 - S "'\r\n\f\\") + (P '\\' * 1)) ^ 0 * "'" 119 | local doublequoted_string = P '"' * ((1 - S '"\r\n\f\\') + (P '\\' * 1)) ^ 0 * '"' 120 | local string = token('string', singlequoted_string + 121 | doublequoted_string + 122 | longstring) 123 | 124 | -- comments 125 | local singleline_comment = P '--' * (1 - S '\r\n\f') ^ 0 126 | local multiline_comment = P '--' * longstring 127 | local comment = token('comment', multiline_comment + singleline_comment) 128 | 129 | -- whitespace 130 | local whitespace = token('whitespace', S('\r\n\f\t ')^1) 131 | 132 | -- ordered choice of all tokens and last-resort error which consumes one character 133 | local any_token = whitespace + number + keyword + ident + 134 | string + comment + operator + token('error', 1) 135 | 136 | -- private interface 137 | local table_of_tokens = Ct(any_token ^ 0) 138 | 139 | -- increment [line] by the number of line-ends in [text] 140 | local function sync(line, text) 141 | local index, limit = 1, #text 142 | while index <= limit do 143 | local start, stop = text:find('\r\n', index, true) 144 | if not start then 145 | start, stop = text:find('[\r\n\f]', index) 146 | if not start then break end 147 | end 148 | index = stop + 1 149 | line = line + 1 150 | end 151 | return line 152 | end 153 | 154 | -- we only need to synchronize the line-counter for these token types 155 | local multiline_tokens = { comment = true, string = true, whitespace = true } 156 | 157 | -- public interface 158 | local function lexer(input) 159 | assert(type(input) == 'string', 'bad argument #1 (expected string)') 160 | local line = 1 161 | local tokens = lpeg.match(table_of_tokens, input) 162 | for i, token in pairs(tokens) do 163 | token[3] = line 164 | if multiline_tokens[token[1]] then line = sync(line, token[2]) end 165 | end 166 | return tokens 167 | end 168 | 169 | -- strip interface 170 | local function strip(s) 171 | local tokens = lexer(s) 172 | local line_head = true 173 | local prev_space = false 174 | local line = {} 175 | for i, v in ipairs(tokens) do 176 | if v[1] == 'comment' or v[1] == 'whitespace' then 177 | if v[2]:match "\n" then 178 | line[#line+1] = v[2]:gsub(".-\n[^\n]*", "\n") 179 | line_head = true 180 | end 181 | prev_space = true 182 | else 183 | if prev_space and not line_head then 184 | line[#line+1] = " " 185 | end 186 | line[#line+1] = v[2] 187 | prev_space = false 188 | line_head = false 189 | end 190 | end 191 | return table.concat(line) 192 | end 193 | 194 | local function tocstring(s, wide) 195 | local texts = {} 196 | local line = {} 197 | local curwide = 0 198 | wide = wide - 6 -- indent and quote 199 | for s in s:gmatch "." do 200 | if s == '\n' then 201 | line[#line+1] = '\\n' 202 | curwide = curwide + 2 203 | elseif s :match '["\\]' then 204 | line[#line+1] = '\\'..s 205 | curwide = curwide + 2 206 | elseif s :match '[\0-\31\128-\255]' then 207 | line[#line+1] = ("\\x%02X"):format(string.byte(s)) 208 | curwide = curwide + 4 209 | else 210 | line[#line+1] = s 211 | curwide = curwide + 1 212 | end 213 | if curwide >= wide then 214 | texts[#texts+1] = ' "' .. table.concat(line) .. '"\n' 215 | line = {} 216 | curwide = 0 217 | end 218 | end 219 | if #line ~= 0 then 220 | texts[#texts+1] = ' "' .. table.concat(line) .. '"\n' 221 | end 222 | return table.concat(texts) 223 | end 224 | 225 | return { 226 | lexer = lexer, 227 | strip = strip, 228 | tocstring = tocstring, 229 | } 230 | -------------------------------------------------------------------------------- /lbind/typeinfo.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | local types = {} 3 | local utils = require 'lbind.utils' 4 | 5 | local function typeref(t) 6 | local refinfo = {} 7 | 8 | -- some fallbacks 9 | setmetatable(refinfo, refinfo) 10 | function refinfo:__index(k) 11 | local self = t:info() 12 | if k == 'type' or k == 'ctype' then 13 | return self.ctype or self.typename 14 | elseif k == 'ltype' then 15 | return self.ltype or self.typename 16 | end 17 | return self[k] 18 | end 19 | 20 | local ref = {} 21 | 22 | function ref:ref(name, idx) 23 | if not refinfo.name then 24 | refinfo.name = idx and name..idx or name 25 | refinfo.arg = utils.template(refinfo.arg_tpl, refinfo) 26 | end 27 | refinfo.idx = idx 28 | return self 29 | end 30 | function ref:opt(value) 31 | refinfo.opt = true 32 | refinfo.default = value 33 | return self 34 | end 35 | 36 | function ref:isopt() return refinfo.opt end 37 | function ref:optvalue() return refinfo.default or refinfo.initvalue end 38 | function ref:opt_check() return refinfo.opt_check_tpl end 39 | function ref:name() return refinfo.name end 40 | function ref:idx() return refinfo.idx end 41 | function ref:ctype() return refinfo.ctype end 42 | function ref:ltype() return refinfo.ltype end 43 | function ref:info() return refinfo end 44 | 45 | function ref:gen_arg() 46 | return refinfo.arg 47 | end 48 | function ref:gen_decl(initvalue) 49 | refinfo.initvalue = initvalue 50 | local ret = utils.template(refinfo.decl_tpl, refinfo).. 51 | utils.template(refinfo.decl_arg, refinfo) 52 | refinfo.initvalue = nil 53 | return ret 54 | end 55 | function ref:gen_assign(value) 56 | refinfo.value = value 57 | local ret = utils.template(refinfo.assign_tpl, refinfo) 58 | refinfo.value = nil 59 | return ret 60 | end 61 | 62 | local function gen_template(name) 63 | ref['gen_'..name] = function(self, narg) 64 | refinfo.narg = narg 65 | local ret = utils.template(refinfo[name.."_tpl"], refinfo) 66 | refinfo.narg = nil 67 | return ret, refinfo[name.."_nstack"] 68 | end 69 | end 70 | gen_template "is" 71 | gen_template "to" 72 | gen_template "check" 73 | gen_template "opt_check" 74 | 75 | function ref:gen_push() 76 | return utils.template(refinfo.push_tpl, refinfo), refinfo.push_nstack 77 | end 78 | 79 | return ref 80 | end 81 | 82 | function M.type(name) 83 | local t = {} 84 | local info = { 85 | typename = name, 86 | initvalue = "0", 87 | arg_tpl = "$name", 88 | assign_tpl = "$name = $value", 89 | decl_tpl = "$type $name", 90 | decl_arg = " = $initvalue", 91 | } 92 | 93 | function t:set(key) 94 | return function(value) 95 | info.key = value 96 | end 97 | end 98 | function t:ctype(name) 99 | info.ctype = name 100 | return self 101 | end 102 | function t:ltype(name) 103 | info.ltype = name 104 | return self 105 | end 106 | function t:initvalue(s) 107 | info.initvalue = utils.trim(s) 108 | return self 109 | end 110 | function t:arg(s) 111 | info.arg_tpl = utils.trim(s) 112 | return self 113 | end 114 | function t:assign(s) 115 | info.assign_tpl = utils.trim(s) 116 | return self 117 | end 118 | function t:decl(decl, arg) 119 | if decl then info.decl_tpl = utils.trim(decl) end 120 | if arg then info.decl_arg = utils.trim(arg) end 121 | return self 122 | end 123 | 124 | local function collect_template(name) 125 | t[name] = function(self, nstack) 126 | if type(nstack) == 'string' then 127 | info[name.."_tpl"] = utils.trim(nstack) 128 | info[name.."_nstack"] = 1 129 | return self 130 | end 131 | return function(s) 132 | info[name.."_tpl"] = utils.trim(s) 133 | info[name.."_nstack"] = nstack 134 | return self 135 | end 136 | end 137 | end 138 | collect_template 'is' 139 | collect_template 'to' 140 | collect_template 'check' 141 | collect_template 'opt_check' 142 | collect_template 'push' 143 | 144 | function t:info() 145 | return info 146 | end 147 | 148 | setmetatable(t, t) 149 | 150 | function t:__call(name) 151 | return typeref(self):ref(name) 152 | end 153 | function t:ref(name, idx) 154 | return typeref(self):ref(name, idx) 155 | end 156 | function t:opt(value) 157 | return typeref(self):opt(value) 158 | end 159 | 160 | types[name] = t 161 | return t 162 | end 163 | 164 | function M.callback(name) 165 | end 166 | 167 | function M.classtype(name) 168 | local t = M.type(name) 169 | 170 | 171 | return t 172 | end 173 | 174 | function M.basetypes(t) 175 | t.int = M.type "int" :ltype "integer" 176 | :is [[ lua_isnumber(L, $narg) ]] 177 | :push [[ lua_pushinteger(L, $name) ]] 178 | :opt_check [[ luaL_optint(L, $narg) ]] 179 | :check [[ luaL_checkint(L, $narg) ]] 180 | :to [[ lua_tointeger(L, $narg) ]] 181 | t.intptr = M.type "intptr" :ltype "integer" :ctype "int" 182 | :arg [[ &$name ]] 183 | :is [[ lua_isnumber(L, $narg) ]] 184 | :push [[ lua_pushinteger(L, $name) ]] 185 | :to [[ lua_tointeger(L, $narg) ]] 186 | t.double = M.type "double" :ltype "number" 187 | :is [[ lbind_isnumber(L, $narg) ]] 188 | :push [[ lua_pushnumber(L, $name) ]] 189 | :to [[ lua_tonumber(L, $narg) ]] 190 | t.cstring = M.type "string" :ltype "string" :ctype "const char *" 191 | :decl [[ $type$name ]] 192 | :is [[ lua_isstring(L, $narg) ]] 193 | :push [[ lua_pushstring(L, $name) ]] 194 | :to [[ lua_tostring(L, $narg) ]] 195 | t.lstring = M.type "lstring" :ltype "string" :ctype "const char *" 196 | :arg [[ $name, ${name}_len ]] 197 | :decl [[ 198 | size_t ${name}_len; 199 | $type$name ]] 200 | :is [[ lua_isstring(L, $narg) ]] 201 | :push [[ lua_pushlstring(L, $name, ${name}_len) ]] 202 | :to [[ lua_tolstring(L, $narg, &${name}_len) ]] 203 | 204 | t.char = t.int 205 | end 206 | 207 | function M.gettype(name) 208 | return types[name] 209 | end 210 | 211 | local function gen_getargs(_, fn, narg, args) 212 | local t = {} 213 | 214 | for i, v in ipairs(args) do 215 | local checkstr, nstack 216 | if v:isopt() and v:opt_check() then 217 | checkstr, nstack = v:gen_opt_check(narg) 218 | else 219 | checkstr, nstack = v[fn](v, narg) 220 | if v:isopt() then 221 | checkstr = ("lua_isnoneornil(L, %d) ? %s : %s") 222 | :format(narg, checkstr, v:optvalue()) 223 | end 224 | end 225 | _(v:gen_decl(checkstr))";" 226 | t[#t + 1] = v:gen_arg() 227 | narg = narg + nstack 228 | end 229 | 230 | return t 231 | end 232 | 233 | function M.gen_getargs(_, narg, args) 234 | return gen_getargs(_, 'gen_to', narg, args) 235 | end 236 | 237 | function M.gen_checkargs(_, narg, args) 238 | return gen_getargs(_, 'gen_check', narg, args) 239 | end 240 | 241 | function M.gen_pushargs(_, args) 242 | local count = 0 243 | for i, v in ipairs(args) do 244 | local pushstr, nstack = v:gen_push() 245 | _(pushstr)";" 246 | count = count + nstack 247 | end 248 | return count 249 | end 250 | 251 | return M 252 | -------------------------------------------------------------------------------- /lbind/types.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ";../?.lua" 2 | local utils = require 'lbind.utils' 3 | local M = {} 4 | local T = {} 5 | 6 | local typeMT, varMT 7 | 8 | local function typedecl(name, tag) 9 | local t = T[name] 10 | if t then return t end 11 | t = setmetatable({ 12 | name = name, 13 | tag = tag or "type", 14 | }, typeMT) 15 | T[name] = t 16 | return t 17 | end 18 | 19 | -- methods in metatables 20 | 21 | local function rawmethod(name) 22 | return function(self, value) 23 | self[name] = value 24 | return self 25 | end 26 | end 27 | 28 | local function boolmethod(name) 29 | return function(self, value) 30 | if value == nil then 31 | value = true 32 | end 33 | self[name] = value 34 | return self 35 | end 36 | end 37 | 38 | local function stringmethod(name) 39 | return function(self, value) 40 | self[name] = utils.trim(value) 41 | return self 42 | end 43 | end 44 | 45 | local function templatemethod(name) 46 | return function(self, nstack) 47 | if type(nstack) == 'string' then 48 | self[name.."_tpl"] = utils.trim(nstack) 49 | self[name.."_nstack"] = 1 50 | return self 51 | end 52 | return function(s) 53 | self[name.."_tpl"] = utils.trim(s) 54 | self[name.."_nstack"] = nstack 55 | return self 56 | end 57 | end 58 | end 59 | 60 | local function find_type(tname, key) 61 | local t = T[tname] 62 | if not t then 63 | t = typedecl(tname, tag) 64 | t[key] = true 65 | elseif not t[key] then 66 | error("imcompatile type: "..tname, 2) 67 | end 68 | return t 69 | end 70 | 71 | local function qualmethod(qual) 72 | local key = "is_"..qual 73 | local prefix = qual.."_" 74 | return function(self) 75 | if self[key] then return self end 76 | return find_type(prefix..self.name, key) 77 | end 78 | end 79 | 80 | local function indirmethod(indir) 81 | local key = "is_"..indir 82 | local postfix = "_"..indir:sub(1,1) 83 | return function(self, name, tag) 84 | if type(name) == 'string' then -- declare new variable 85 | local var = self(name, tag) 86 | var[key] = true 87 | return var 88 | end 89 | 90 | if self[key] then return self end 91 | return find_type(self.name..postfix, key) 92 | end 93 | end 94 | 95 | typeMT = { 96 | extends = rawmethod 'base', 97 | ctype = stringmethod 'type_c', 98 | ltype = stringmethod 'type_lua', 99 | const = qualmethod 'const', 100 | volatile = qualmethod 'volatile', 101 | ptr = indirmethod 'ptr', 102 | ref = indirmethod 'ref', 103 | arg = stringmethod 'arg_tpl', 104 | assign = stringmethod 'assign_tpl', 105 | initvalue = stringmethod 'initvalue_tpl', 106 | check = templatemethod "check", 107 | is = templatemethod "is", 108 | opt = templatemethod "opt", 109 | push = templatemethod "push", 110 | to = templatemethod "to", 111 | } 112 | typeMT.__index = typeMT 113 | 114 | function typeMT:set(key) 115 | if type(key) == 'table' then 116 | for k, v in pairs(key) do 117 | self[k] = v 118 | end 119 | else 120 | return function(value) 121 | self[key] = value 122 | return self 123 | end 124 | end 125 | return self 126 | end 127 | 128 | varMT = { 129 | const = boolmethod 'is_const', 130 | volatile = boolmethod 'is_volatile', 131 | ptr = boolmethod "is_ptr", 132 | ref = boolmethod "is_ref", 133 | opt = stringmethod "opt_tpl", 134 | } 135 | varMT.__index = varMT 136 | 137 | -- using __call, ptr, ref to create var instance. 138 | function typeMT:__call(name, tag) 139 | return setmetatable({ 140 | tag = tag or "var", 141 | name = name, 142 | type = self, 143 | }, varMT) 144 | end 145 | 146 | local function inttype(name, ctype) 147 | return typedecl(name) 148 | :ctype(ctype or name) 149 | :ltype "integer" 150 | :is "lua_isnumber(L, $narg)" 151 | :push "lua_pushinteger(L, $name)" 152 | :opt "luaL_optint(L, $narg, $defaultvalue)" 153 | :check "luaL_checkint(L, $narg)" 154 | :to "($ctype)lua_tointeger(L, $narg)" 155 | end 156 | 157 | local function fixinttype(len, u) 158 | return inttype((u or "").."int"..len.."_t") 159 | end 160 | 161 | local function numbertype(name, ctype) 162 | return typedecl(name) 163 | :ctype(ctype or name) 164 | :ltype "number" 165 | :is "lua_isnumber(L, $narg)" 166 | :push "lua_pushnumber(L, $name)" 167 | :opt "luaL_optnumber(L, $narg, $defaultvalue)" 168 | :check "luaL_checknumber(L, $narg)" 169 | :to "($ctype)lua_tonumber(L, $narg)" 170 | end 171 | 172 | local function stringtype(name, ctype) 173 | return typedecl(name) 174 | :ctype(ctype or name) 175 | :ltype "string" 176 | :is "lua_isstring(L, $narg)" 177 | :push "lua_pushstring(L, $name)" 178 | :opt "luaL_optstring(L, $narg, $defaultvalue)" 179 | :check "luaL_checkstring(L, $narg)" 180 | :to "($ctype)lua_tostring(L, $narg)" 181 | end 182 | 183 | local function classtype(name, ctype) 184 | local t = typedecl(name):ctype(ctype or name) 185 | return t 186 | end 187 | 188 | -- C part 189 | inttype "char" :ctype "char" 190 | inttype "uchar" :ctype "unsigned char" 191 | inttype "short" :ctype "short int" 192 | inttype "ushort" :ctype "unsigned short int" 193 | inttype "int" :ctype "int" 194 | inttype "uint" :ctype "unsigned int" 195 | inttype "long" :ctype "long int" 196 | inttype "ulong" :ctype "unsigned long int" 197 | inttype "size_t" 198 | inttype "ssize_t" 199 | fixinttype(8) 200 | fixinttype(8, "u") 201 | fixinttype(16) 202 | fixinttype(16, "u") 203 | fixinttype(32) 204 | fixinttype(32, "u") 205 | fixinttype(64) 206 | fixinttype(64, "u") 207 | numbertype "float" 208 | numbertype "double" 209 | numbertype "ldouble" :ctype "long double" 210 | stringtype "char_p" :ctype "char *" 211 | stringtype "const_char_p" :ctype "const char *" 212 | stringtype "uchar_p" :ctype "unsigned char *" 213 | 214 | -- C++ part 215 | 216 | function M.export(t) 217 | for k, v in pairs(M) do 218 | t[k] = v 219 | end 220 | M.basetypes(t) 221 | return M 222 | end 223 | 224 | function M.dyn_export(t) 225 | local mt = getmetatable(t) 226 | local old_index = mt.__index 227 | if old_index then 228 | if type(old_index) == 'table' then 229 | function mt:__index(key) 230 | return old_index[key] or T[key] 231 | end 232 | elseif type(old_index) == 'function'then 233 | function mt:__index(key) 234 | return old_index(self, key) or T[key] 235 | end 236 | end 237 | else 238 | mt.__index = T 239 | end 240 | return M 241 | end 242 | 243 | function M.basetypes(t) 244 | if t then 245 | for k, v in pairs(T) do 246 | t[k] = v 247 | end 248 | end 249 | return T 250 | end 251 | 252 | function M.typedecl(name) 253 | local t = typedecl(name) 254 | T[name] = t 255 | return t 256 | end 257 | 258 | function M.class(name) 259 | local t = classtype(name) 260 | return t 261 | end 262 | 263 | M.selfType = classtype "" 264 | 265 | return M 266 | -------------------------------------------------------------------------------- /lbind/utils.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | local function expandtab(s) 4 | local space, line = s :match "^(%s*)(.-)%s*$" 5 | local col = 0 6 | string.gsub(space, '.', function(s) 7 | if s == "\t" then 8 | col = math.ceil((col + 1)/8) * 8 9 | else 10 | col = col + 1 11 | end 12 | end) 13 | return (" "):rep(col)..line 14 | end 15 | 16 | local function splitlines(t, s) 17 | if type(s) == 'table' then s = table.concat(s) end 18 | local indent = expandtab(s):match "^(%s*)" 19 | s = string.gsub(s, "(.-)\r*\n", function(s) 20 | local line = expandtab(s):match("^"..indent.."(.-)%s*$") 21 | t[#t+1] = line or s 22 | return "" 23 | end) 24 | local s = expandtab(s) 25 | return t, s:match("^"..indent.."(.-)$") or s:match "^%s*(.-)%s*$" 26 | end 27 | 28 | --- Output String builder constructoer. 29 | -- this function is used to create a string builder with a table as 30 | -- string pool. 31 | -- 32 | -- local pool = {} 33 | -- local L = utils.builder(pool) 34 | -- 35 | -- a string builder is used to create a big string, somethimes used to 36 | -- create program text, a string builder is a function, that accept a 37 | -- number or a string: 38 | -- - if a number is given, it will added to indent the lines in 39 | -- builder. 40 | -- L(4) -- indent is 4 41 | -- L(4) -- indent is 8 42 | -- L(-4) -- indent is 4 43 | -- - if a string is given, it will be appended to current line, and a 44 | -- appender will be returned, that means you can use chained 45 | -- expression: 46 | -- L"hello" " " "World" "!" -- a line "Hello World!" 47 | -- - every call to builder start a new line: 48 | -- L"Hello" 49 | -- L"World" -- two line "Hello\nWorld" 50 | -- @param t a string pool used to contain string pieces, to get the 51 | -- big string itself, just use table.concat(t). 52 | function M.builder(t) 53 | t = t or {} 54 | local lvl = 0 55 | local function appender(...) 56 | t[#t] = t[#t] .. table.concat {...} 57 | local s = t[#t] 58 | if s :match "\n" then 59 | local lvls = (" "):rep(lvl) 60 | local last = #t 61 | s = s:sub(lvl+1) 62 | t[#t] = nil 63 | local t, remains = splitlines(t, s) 64 | for i = last, #t do 65 | t[i] = t[i]:match "^%s*$" and "" or lvls..t[i] 66 | end 67 | if remains ~= "" then 68 | t[#t+1] = lvls..remains 69 | end 70 | end 71 | return appender 72 | end 73 | local function header(indent, ...) 74 | if type(indent) == "number" then 75 | lvl = lvl + indent 76 | return header 77 | end 78 | if type(indent) == 'table' then 79 | local lvls = (" "):rep(lvl) 80 | for k, v in ipairs(indent) do 81 | t[#t + 1] = lvls..v 82 | end 83 | if t[#t] == "" then 84 | t[#t] = nil 85 | end 86 | return header 87 | end 88 | if not indent then return t end 89 | local preline = t[#t] 90 | if preline and preline :match "^%s*$" then 91 | t[#t] = "" 92 | end 93 | t[#t + 1] = lvl and (" "):rep(lvl) or "" 94 | return appender(indent, ...) 95 | end 96 | return header, appender 97 | end 98 | 99 | function M.getlines(s) 100 | local t, remains = splitlines({}, s) 101 | if remains ~= "" then 102 | t[#t + 1] = remains 103 | end 104 | return t 105 | end 106 | 107 | function M.indent(idt, s) 108 | local idts = (" "):rep(idt) 109 | local s = table.concat(M.getlines(s), "\n"..idts) 110 | if s ~= "" then 111 | s = idts .. s 112 | end 113 | return s--:match "^\n*(.-)[ \n]*$" 114 | end 115 | 116 | function M.trim(s) 117 | if s:match "\n" then 118 | local t, remains = splitlines({}, s) 119 | return table.concat(t, "\n").."\n"..remains 120 | end 121 | return s:match "^%s*(.-)%s*$" 122 | end 123 | 124 | local function template(tpl, info, blacklist, cache) 125 | local function helper(s) 126 | if not info[s] or blacklist[s] then return end 127 | if cache and cache[s] then return cache[s] end 128 | blacklist[s] = true 129 | local ret = template(info[s], info, blacklist) 130 | if cache then cache[s] = ret end 131 | blacklist[s] = nil 132 | return ret 133 | end 134 | if type(tpl) ~= 'string' or not tpl :match "%$" then 135 | return tpl 136 | end 137 | return (tpl:gsub("${(%w+)}", helper) 138 | :gsub("$(%w+)", helper)) 139 | end 140 | 141 | function M.template(tpl, info) 142 | return template(tpl, info, {}, {}) 143 | end 144 | 145 | local function table_tostring(t, lvl) 146 | local ttype = type(t) 147 | if ttype == 'string' then 148 | return ('%q'):format(t) 149 | elseif ttype == 'table' then 150 | local lvlstring = (' '):rep(lvl) 151 | local lvl2string = (' '):rep(lvl+1) 152 | local st = {"{\n"} 153 | for k, v in pairs(t) do 154 | local cur = #st 155 | st[cur + 1] = lvl2string 156 | st[cur + 2] = "[" 157 | st[cur + 3] = table_tostring(k, lvl+1) 158 | st[cur + 4] = "] = " 159 | st[cur + 5] = table_tostring(v, lvl+1) 160 | st[cur + 6] = ",\n" 161 | end 162 | st[#st + 1] = lvlstring 163 | st[#st + 1] = "}" 164 | return table.concat(st) 165 | end 166 | return tostring(t) 167 | end 168 | 169 | function M.tostring(t) 170 | return table_tostring(t, 0) 171 | end 172 | 173 | return M 174 | -------------------------------------------------------------------------------- /runtime/lbind.c: -------------------------------------------------------------------------------- 1 | #define LBIND_IMPLEMENTATION 2 | #include "lbind.h" 3 | /* cc: lua='lua53' output='lbind.dll' 4 | * cc: flags+='-s -O2 -Wall -std=c99 -pedantic -mdll -Id:/$lua/include' 5 | * cc: run='$lua tt.lua' libs+='-L D:/$lua -l$lua' */ 6 | -------------------------------------------------------------------------------- /runtime/lbind.h: -------------------------------------------------------------------------------- 1 | #ifndef LBIND_H 2 | #define LBIND_H 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | #if LUA_VERSION_NUM >= 503 10 | # define lua53_getuservalue lua_getuservalue 11 | # define lua53_gettable lua_gettable 12 | # define lua53_getfield lua_getfield 13 | # define lua53_rawget lua_rawget 14 | # define lua53_rawgeti lua_rawgeti 15 | # define lua53_rawgetp lua_rawgetp 16 | # define lua53_rotate lua_rotate 17 | #else 18 | LUA_API int lua53_getuservalue(lua_State *L, int idx); 19 | LUA_API int lua53_gettable(lua_State *L, int idx); 20 | LUA_API int lua53_getfield(lua_State *L, int idx, const char *field); 21 | LUA_API int lua53_rawget(lua_State *L, int idx); 22 | LUA_API int lua53_rawgeti(lua_State *L, int idx, lua_Integer n); 23 | LUA_API int lua53_rawgetp(lua_State *L, int idx, const void *p); 24 | LUA_API void lua53_rotate(lua_State *L, int idx, int n); 25 | #endif 26 | 27 | #if LUA_VERSION_NUM < 502 28 | # define LUA_OK 0 29 | # define lua_getuservalue lua_getfenv 30 | # define lua_setuservalue lua_setfenv 31 | # define lua_rawlen lua_objlen 32 | 33 | # define luaL_newlibtable(L,l) \ 34 | lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) 35 | # define luaL_newlib(L,l) \ 36 | (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) 37 | 38 | LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *valid); 39 | LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); 40 | LUA_API void (lua_rawgetp) (lua_State *L, int idx, const void *p); 41 | LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); 42 | LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); 43 | #endif /* LUA_VERSION_NUM */ 44 | 45 | #ifdef __cplusplus 46 | # define LB_NS_BEGIN extern "C" { 47 | # define LB_NS_END } 48 | #else 49 | # define LB_NS_BEGIN 50 | # define LB_NS_END 51 | #endif 52 | 53 | #ifdef LBIND_STATIC_API 54 | # ifndef LBIND_IMPLEMENTATION 55 | # define LBIND_IMPLEMENTATION 56 | # endif 57 | # if __GNUC__ 58 | # define LB_API static __attribute((unused)) 59 | # else 60 | # define LB_API static 61 | # endif 62 | #endif 63 | 64 | #if !defined(LB_API) && defined(_WIN32) 65 | # ifdef LBIND_IMPLEMENTATION 66 | # define LB_API __declspec(dllexport) 67 | # else 68 | # define LB_API __declspec(dllimport) 69 | # endif 70 | #endif 71 | 72 | #ifndef LB_API 73 | # define LB_API extern 74 | #endif 75 | 76 | #if defined(_WIN32) 77 | # define LBLIB_API __declspec(dllexport) 78 | #else 79 | # define LBLIB_API extern 80 | #endif 81 | 82 | /* lbind internal max alignment */ 83 | #ifndef LBIND_MAXALIGN 84 | # define LBIND_MAXALIGN union { double u; void *s; long l; } 85 | #endif 86 | 87 | LB_NS_BEGIN 88 | 89 | 90 | typedef LBIND_MAXALIGN lbind_MaxAlign; 91 | 92 | 93 | /* lbind runtime */ 94 | #ifndef LBIND_NO_RUNTIME 95 | LBLIB_API int luaopen_lbind (lua_State *L); 96 | #endif /* LBIND_NO_RUNTIME */ 97 | 98 | 99 | /* lbind utils functions */ 100 | LB_API int lbind_relindex (int idx, int onstack); 101 | LB_API int lbind_argferror (lua_State *L, int idx, const char *fmt, ...); 102 | LB_API int lbind_typeerror (lua_State *L, int idx, const char *tname); 103 | LB_API int lbind_matcherror (lua_State *L, const char *extramsg); 104 | LB_API int lbind_copystack (lua_State *from, lua_State *to, int nargs); 105 | LB_API int lbind_hasfield (lua_State *L, int idx, const char *field); 106 | LB_API int lbind_self (lua_State *L, const void *p, const char *method, int nargs, int *ptraceback); 107 | LB_API int lbind_pcall (lua_State *L, int nargs, int nrets); 108 | 109 | LB_API const char *lbind_dumpstack (lua_State *L, const char *extramsg); 110 | 111 | #define lbind_returnself(L) do { lua_settop((L), 1); return 1; } while (0) 112 | 113 | #define lbind_printstack(L, msg) ( printf("%s\n", lbind_dumpstack((L), (msg))), lua_pop((L), 1) ) 114 | 115 | 116 | /* lbind lua module install */ 117 | typedef struct lbind_Reg { 118 | const char *name; /* name of library */ 119 | lua_CFunction open_func; /* luaopen_ function of library */ 120 | } lbind_Reg; 121 | 122 | LB_API void lbind_install (lua_State *L, lbind_Reg *reg); 123 | LB_API int lbind_requiref (lua_State *L, const char *name, lua_CFunction loader); 124 | LB_API void lbind_requirelibs (lua_State *L, lbind_Reg *reg); 125 | LB_API void lbind_requireinto (lua_State *L, const char *prefix, lbind_Reg *reg); 126 | 127 | 128 | /* metatable maintain */ 129 | LB_API int lbind_setmetatable (lua_State *L, const void *t); 130 | LB_API int lbind_getmetatable (lua_State *L, const void *t); 131 | LB_API int lbind_setmetafield (lua_State *L, int idx, const char *field); 132 | LB_API int lbind_setlibcall (lua_State *L, const char *method); 133 | 134 | #define LBIND_INDEX 0x01 135 | #define LBIND_NEWINDEX 0x02 136 | 137 | LB_API void lbind_setaccessors (lua_State *L, int ntables, int field); 138 | LB_API void lbind_setarrayf (lua_State *L, lua_CFunction f, int field); 139 | LB_API void lbind_sethashf (lua_State *L, lua_CFunction f, int field); 140 | LB_API void lbind_setmaptable (lua_State *L, luaL_Reg libs[], int field); 141 | 142 | #define lbind_checkreadonly(L) ((void)( \ 143 | lua_gettop(L)!=2 && \ 144 | luaL_error((L), "field %s is read-only", \ 145 | lbind_tostring((L), 2)))) 146 | 147 | #define lbind_checkwriteonly(L) ((void)( \ 148 | lua_gettop(L)!=3 && \ 149 | luaL_error((L), "field %s is write-only", \ 150 | lbind_tostring((L), 2)))) 151 | 152 | 153 | /* light userdata utils */ 154 | LB_API int lbind_getudtypebox (lua_State *L); 155 | LB_API int lbind_getlightuservalue (lua_State *L, const void *p); 156 | LB_API void lbind_setlightuservalue (lua_State *L, const void *p); 157 | 158 | 159 | /* lbind peer table support, define LBIND_NO_PEER to disable this. 160 | * 161 | * peer table is a normal table that has a field "__peer", t.__peer is 162 | * the real userdata, in this case, lbind use this table as it is 163 | * t.__peer. i.e. lbind treat that table as a native object. 164 | */ 165 | LB_API void *lbind_touserdata (lua_State *L, int idx); 166 | 167 | 168 | /* lbind class runtime */ 169 | 170 | /* 171 | * NOTE: all types must have a global Type structure. its address used 172 | * to find the right informations about type. So all const lbind_Type *t 173 | * argument must be &var, where var is declared by LB_API, e.g. 174 | * LB_API lbind_Type basetype; 175 | */ 176 | typedef struct lbind_Type lbind_Type; 177 | 178 | typedef void *lbind_Cast(lua_State *L, int idx, const lbind_Type *to_type); 179 | 180 | struct lbind_Type { 181 | const char *name; 182 | int flags; 183 | lbind_Cast *cast; 184 | lbind_Type **bases; 185 | }; 186 | 187 | /* lbind type registry 188 | * 189 | * a lbind object can tracked and interned. 190 | * 191 | * If a object is tracked, when it collected (i.e. not used any more) 192 | * it's delete function in metatable will called. otherwise it won't 193 | * deleted by Lua, i.e. it's life-time is not associate with Lua. 194 | * 195 | * When a object is interned, You can use it's pointer to find the 196 | * object itself. 197 | * 198 | * if a type has flags LBIND_ACCESSOR, it has a non-trival __index and 199 | * __newindex, that means the object of this type has the ability to 200 | * save any value into it's uservalue and can have custom accessors. 201 | * if a type has base type, it also have LBIND_ACCESSOR flag. 202 | */ 203 | #define LBIND_TRACK 0x01 204 | #define LBIND_INTERN 0x02 205 | #define LBIND_ACCESSOR 0x04 206 | 207 | #ifndef LBIND_DEFAULT_FLAG 208 | # define LBIND_DEFAULT_FLAG (LBIND_TRACK) 209 | #endif 210 | 211 | #define LBIND_INIT(name) { name, LBIND_DEFAULT_FLAG, NULL, NULL } 212 | #define LBIND_TYPE(var, name) LB_API lbind_Type var = LBIND_INIT(name) 213 | 214 | LB_API void lbind_inittype (lbind_Type *t, const char *name); 215 | LB_API void lbind_setbase (lbind_Type *t, lbind_Type **bases, lbind_Cast *cast); 216 | LB_API int lbind_settrack (lbind_Type *t, int autotrack); 217 | LB_API int lbind_setintern (lbind_Type *t, int autointern); 218 | 219 | /* lbind type metatable */ 220 | LB_API int lbind_newmetatable (lua_State *L, luaL_Reg *libs, const lbind_Type *t); 221 | LB_API void lbind_setagency (lua_State *L); 222 | 223 | /* get lbind_Type* from metatable */ 224 | LB_API lbind_Type *lbind_typeobject (lua_State *L, int idx); 225 | 226 | /* lbind type system */ 227 | LB_API const char *lbind_tolstring (lua_State *L, int idx, size_t *plen); 228 | LB_API const char *lbind_type (lua_State *L, int idx); 229 | 230 | LB_API int lbind_isa (lua_State *L, int idx, const lbind_Type *t); 231 | LB_API int lbind_copy (lua_State *L, const void *p, const lbind_Type *t); 232 | LB_API void *lbind_cast (lua_State *L, int idx, const lbind_Type *t); 233 | LB_API void *lbind_check (lua_State *L, int idx, const lbind_Type *t); 234 | LB_API void *lbind_test (lua_State *L, int idx, const lbind_Type *t); 235 | 236 | #define lbind_opt(L,idx,defs,t) \ 237 | (lua_isnoneornil((L),(idx)) ? (defs) : lbind_check((L),(idx),(t))) 238 | 239 | #define lbind_tostring(L,idx) lbind_tolstring((L),(idx),NULL) 240 | 241 | /* lbind object creation 242 | * `lbind_raw` create a raw lbind object, not associate with a 243 | * lbind_Type, if `intern` is non-zero, intern it. 244 | * `lbind_new` create a lbind object associated with a lbind_Type, 245 | * this type decide whether the object is signed up. 246 | * `lbind_wrap` wrap a pointer to lbind object associated with 247 | * lbind_Type, the type decide the signing. 248 | */ 249 | LB_API void *lbind_raw (lua_State *L, size_t objsize, int intern); 250 | LB_API void *lbind_new (lua_State *L, size_t objsize, const lbind_Type *t); 251 | LB_API void *lbind_wrap (lua_State *L, void *p, const lbind_Type *t); 252 | 253 | /* delete a lbind object. unsign, clear and remove metatable of it. */ 254 | LB_API void *lbind_delete (lua_State *L, int idx); 255 | 256 | /* get pointer from a lbind object, or NULL. */ 257 | LB_API void *lbind_object (lua_State *L, int idx); 258 | 259 | /* intern a object with a pointer p, object is on stack. */ 260 | LB_API void lbind_intern (lua_State *L, const void *p); 261 | 262 | /* get lbind object userdata from object pointer. 263 | * require interned before */ 264 | LB_API int lbind_retrieve (lua_State *L, const void *p); 265 | 266 | /* track/untrack object */ 267 | LB_API void lbind_track (lua_State *L, int idx); 268 | LB_API void lbind_untrack (lua_State *L, int idx); 269 | LB_API int lbind_hastrack (lua_State *L, int idx); 270 | 271 | 272 | /* lbind enum runtime */ 273 | #ifndef LBIND_NO_ENUM 274 | 275 | typedef struct lbind_EnumItem { 276 | const char *name; 277 | int value; 278 | } lbind_EnumItem; 279 | 280 | typedef struct lbind_Enum { 281 | const char *name; 282 | size_t nitem; 283 | lbind_EnumItem *items; 284 | } lbind_Enum; 285 | 286 | #define LBIND_INITENUM(name, es) { name, sizeof(es)/sizeof((es)[0]), es } 287 | #define LBIND_ENUM(var, name, es) LB_API lbind_Enum var = LBIND_INITENUM(name, es) 288 | 289 | LB_API void lbind_initenum (lbind_Enum *et, const char *name); 290 | 291 | LB_API lbind_EnumItem *lbind_findenum (lbind_Enum *et, const char *s, size_t len); 292 | 293 | LB_API int lbind_pushenum (lua_State *L, const char *name, lbind_Enum *et); 294 | LB_API int lbind_testenum (lua_State *L, int idx, lbind_Enum *et); 295 | LB_API int lbind_checkenum (lua_State *L, int idx, lbind_Enum *et); 296 | 297 | LB_API int lbind_pushmask (lua_State *L, int evalue, lbind_Enum *et); 298 | LB_API int lbind_testmask (lua_State *L, int idx, lbind_Enum *et); 299 | LB_API int lbind_checkmask (lua_State *L, int idx, lbind_Enum *et); 300 | 301 | #define lbind_optenum(L,idx,defs,t) \ 302 | (lua_isnoneornil((L),(idx)) ? (defs) : lbind_checkenum((L),(idx),(t))) 303 | 304 | #define lbind_optmask(L,idx,defs,t) \ 305 | (lua_isnoneornil((L),(idx)) ? (defs) : lbind_checkmask((L),(idx),(t))) 306 | 307 | 308 | #endif /* LBIND_NO_ENUM */ 309 | 310 | 311 | LB_NS_END 312 | 313 | #endif /* LBIND_H */ 314 | 315 | #ifdef LBIND_IMPLEMENTATION 316 | 317 | 318 | #include 319 | 320 | LB_NS_BEGIN 321 | 322 | 323 | #if LUA_VERSION_NUM < 503 324 | LUA_API int lua53_getuservalue(lua_State *L, int idx) 325 | { lua_getuservalue(L, idx); return lua_type(L, -1); } 326 | LUA_API int lua53_gettable(lua_State *L, int idx) 327 | { lua_gettable(L, idx); return lua_type(L, -1); } 328 | LUA_API int lua53_getfield(lua_State *L, int idx, const char *field) 329 | { lua_getfield(L, idx, field); return lua_type(L, -1); } 330 | LUA_API int lua53_rawget(lua_State *L, int idx) 331 | { lua_rawget(L, idx); return lua_type(L, -1); } 332 | LUA_API int lua53_rawgeti(lua_State *L, int idx, lua_Integer n) 333 | { lua_rawgeti(L, idx, n); return lua_type(L, -1); } 334 | LUA_API int lua53_rawgetp(lua_State *L, int idx, const void *p) 335 | { lua_rawgetp(L, idx, p); return lua_type(L, -1); } 336 | 337 | LUA_API void lua53_rotate(lua_State *L, int idx, int n) { 338 | int i; 339 | if (n < 0) 340 | n += (idx < 0) ? -idx : (lua_gettop(L)-idx-1); 341 | for (i = 0; i < n; ++i) 342 | lua_insert(L, -idx); 343 | } 344 | #endif 345 | 346 | 347 | /* compatible apis */ 348 | #if LUA_VERSION_NUM < 502 349 | LUA_API lua_Integer lua_tointegerx(lua_State *L, int idx, int *valid) { 350 | lua_Integer n; 351 | *valid = (n = lua_tointeger(L, idx)) != 0 || lua_isnumber(L, idx); 352 | return n; 353 | } 354 | 355 | LUA_API void lua_rawgetp(lua_State *L, int idx, const void *p) { 356 | lua_pushlightuserdata(L, (void*)p); 357 | lua_rawget(L, lbind_relindex(idx, 1)); 358 | } 359 | 360 | LUA_API void lua_rawsetp(lua_State *L, int idx, const void *p) { 361 | lua_pushlightuserdata(L, (void*)p); 362 | lua_insert(L, -2); 363 | lua_rawset(L, lbind_relindex(idx, 1)); 364 | } 365 | 366 | LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) { 367 | luaL_checkstack(L, nup, "too many upvalues"); 368 | for (; l->name != NULL; l++) { /* fill the table with given functions */ 369 | int i; 370 | for (i = 0; i < nup; i++) /* copy upvalues to the top */ 371 | lua_pushvalue(L, -nup); 372 | lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ 373 | lua_setfield(L, -(nup + 2), l->name); 374 | } 375 | lua_pop(L, nup); /* remove upvalues */ 376 | } 377 | 378 | LUALIB_API const char *luaL_tolstring(lua_State *L, int idx, size_t *plen) { 379 | if (!luaL_callmeta(L, idx, "__tostring")) { /* no metafield? */ 380 | switch (lua_type(L, idx)) { 381 | case LUA_TNUMBER: 382 | case LUA_TSTRING: 383 | lua_pushvalue(L, idx); 384 | break; 385 | case LUA_TBOOLEAN: 386 | lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false")); 387 | break; 388 | case LUA_TNIL: 389 | lua_pushliteral(L, "nil"); 390 | break; 391 | default: 392 | lua_pushfstring(L, "%s: %p", luaL_typename(L, idx), 393 | lua_topointer(L, idx)); 394 | break; 395 | } 396 | } 397 | return lua_tolstring(L, -1, plen); 398 | } 399 | 400 | /* LuaJIT has its own luaL_traceback(), 401 | * so we do not export this, use static instead. */ 402 | #ifdef LUA_BITSINT /* not LuaJIT */ 403 | #define LEVELS1 12 /* size of the first part of the stack */ 404 | #define LEVELS2 10 /* size of the second part of the stack */ 405 | static void luaL_traceback(lua_State *L, lua_State *L1, const char *msg, int level) { 406 | int top = lua_gettop(L); 407 | int firstpart = 1; /* still before eventual `...' */ 408 | lua_Debug ar; 409 | if (msg) lua_pushfstring(L, "%s\n", msg); 410 | lua_pushliteral(L, "stack traceback:"); 411 | while (lua_getstack(L1, level++, &ar)) { 412 | if (level > LEVELS1 && firstpart) { 413 | /* no more than `LEVELS2' more levels? */ 414 | if (!lua_getstack(L1, level+LEVELS2, &ar)) 415 | level--; /* keep going */ 416 | else { 417 | lua_pushliteral(L, "\n\t..."); /* too many levels */ 418 | while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ 419 | level++; 420 | } 421 | firstpart = 0; 422 | continue; 423 | } 424 | lua_pushliteral(L, "\n\t"); 425 | lua_getinfo(L1, "Snl", &ar); 426 | lua_pushfstring(L, "%s:", ar.short_src); 427 | if (ar.currentline > 0) 428 | lua_pushfstring(L, "%d:", ar.currentline); 429 | if (*ar.namewhat != '\0') /* is there a name? */ 430 | lua_pushfstring(L, " in function " LUA_QS, ar.name); 431 | else { 432 | if (*ar.what == 'm') /* main? */ 433 | lua_pushfstring(L, " in main chunk"); 434 | else if (*ar.what == 'C' || *ar.what == 't') 435 | lua_pushliteral(L, " ?"); /* C function or tail call */ 436 | else 437 | lua_pushfstring(L, " in function <%s:%d>", 438 | ar.short_src, ar.linedefined); 439 | } 440 | lua_concat(L, lua_gettop(L) - top); 441 | } 442 | lua_concat(L, lua_gettop(L) - top); 443 | } 444 | #endif /* LUA_BITSINT */ 445 | 446 | #endif /* LUA_VERSION_NUM < 502 */ 447 | 448 | 449 | /* lbind information hash routine */ 450 | 451 | #define LBIND_PTRBOX 0x90127B07 452 | #define LBIND_TYPEBOX 0x799E0B07 453 | #define LBIND_UDBOX 0xC5E7DB07 454 | 455 | static int lbB_retrieve(lua_State *L, unsigned id) { 456 | if (lua53_rawgetp(L, LUA_REGISTRYINDEX, (void*)(ptrdiff_t)id) == LUA_TNIL) { 457 | lua_pop(L, 1); 458 | lua_newtable(L); 459 | lua_pushvalue(L, -1); 460 | lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)(ptrdiff_t)id); 461 | return 1; 462 | } 463 | return 0; 464 | } 465 | 466 | static void lbB_internbox(lua_State *L) { 467 | if (lbB_retrieve(L, LBIND_PTRBOX)) { 468 | lua_pushliteral(L, "v"); 469 | lbind_setmetafield(L, -2, "__mode"); 470 | } 471 | } 472 | 473 | static void lbB_typebox(lua_State *L) { 474 | lbB_retrieve(L, LBIND_TYPEBOX); 475 | } 476 | 477 | 478 | /* light userdata utils */ 479 | 480 | LB_API int lbind_getudtypebox(lua_State *L) { 481 | return lbB_retrieve(L, LBIND_UDBOX); 482 | } 483 | 484 | LB_API void lbind_setlightuservalue(lua_State *L, const void *p) { 485 | lbind_getudtypebox(L); 486 | lua_pushvalue(L, -2); 487 | lua_rawsetp(L, -2, p); 488 | lua_pop(L, 1); 489 | } 490 | 491 | LB_API int lbind_getlightuservalue(lua_State *L, const void *p) { 492 | lbind_getudtypebox(L); 493 | if (lua53_rawgetp(L, -1, p) == LUA_TNIL) { 494 | lua_pop(L, 2); 495 | return 0; 496 | } 497 | lua_remove(L, 2); 498 | return 1; 499 | } 500 | 501 | 502 | /* lbind class register */ 503 | 504 | LB_API void lbind_install(lua_State *L, lbind_Reg *libs) { 505 | lua_getfield(L, LUA_REGISTRYINDEX, "_PRELOAD"); /* 1 */ 506 | if (libs != NULL) { 507 | for (; libs->name != NULL; ++libs) { 508 | lua_pushstring(L, libs->name); /* 2 */ 509 | lua_pushcfunction(L, libs->open_func); /* 3 */ 510 | lua_rawset(L, -3); /* 2,3->1 */ 511 | } 512 | } 513 | #ifndef LBIND_NO_RUNTIME 514 | lua_pushstring(L, "lbind"); /* 2 */ 515 | lua_pushcfunction(L, luaopen_lbind); /* 3 */ 516 | lua_rawset(L, -3); /* 2,3->1 */ 517 | #endif 518 | lua_pop(L, 1); /* (1) */ 519 | } 520 | 521 | LB_API int lbind_requiref(lua_State *L, const char *name, lua_CFunction loader) { 522 | lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); /* 1 */ 523 | if (lua53_getfield(L, -1, name) != LUA_TNIL) { /* 2 */ 524 | lua_remove(L, -2); /* (1) */ 525 | return 0; 526 | } 527 | lua_pop(L, 1); 528 | lua_pushstring(L, name); /* 2 */ 529 | lua_pushcfunction(L, loader); /* 3 */ 530 | lua_pushvalue(L, -2); /* 2->4 */ 531 | lua_call(L, 1, 1); /* 3,4->3 */ 532 | lua_pushvalue(L, -1); /* 3->4 */ 533 | lua_insert(L, -4); /* 4->1 */ 534 | /* stack: lib _LOADED name lib */ 535 | lua_rawset(L, -3); /* 3,4->2 */ 536 | lua_pop(L, 1); /* (2) */ 537 | return 1; 538 | } 539 | 540 | LB_API void lbind_requirelibs(lua_State *L, lbind_Reg *libs) { 541 | lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); /* 1 */ 542 | for (; libs->name != NULL; ++libs) { 543 | lua_pushstring(L, libs->name); /* 2 */ 544 | lua_pushvalue(L, -1); /* 3 */ 545 | if (lua53_rawget(L, -3) != LUA_TNIL) { /* 3->3 */ 546 | lua_pop(L, 2); 547 | continue; 548 | } 549 | lua_pop(L, 1); 550 | lua_pushcfunction(L, libs->open_func); /* 3 */ 551 | lua_pushvalue(L, -2); /* 2->4 */ 552 | lua_call(L, 1, 1); /* 3,4->3 */ 553 | lua_rawset(L, -5); /* 2,3->1 */ 554 | } 555 | lua_pop(L, 1); 556 | } 557 | 558 | LB_API void lbind_requireinto(lua_State *L, const char *prefix, lbind_Reg *libs) { 559 | /* stack: table */ 560 | lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); /* 1 */ 561 | for (; libs->name != NULL; ++libs) { 562 | lua_pushstring(L, libs->name); /* 2 */ 563 | if (prefix == NULL) 564 | lua_pushvalue(L, -1); /* 3 */ 565 | else 566 | lua_pushfstring(L, "%s.%s", prefix, libs->name); /* 3 */ 567 | lua_pushvalue(L, -1); /* 4 */ 568 | if (lua53_rawget(L, -4) == LUA_TNIL) { /* 4->4 */ 569 | lua_pop(L, 1); /* (4) */ 570 | lua_pushcfunction(L, libs->open_func); /* 4 */ 571 | lua_pushvalue(L, -2); /* 3->5 */ 572 | lua_call(L, 1, 1); /* 4,5->4 */ 573 | lua_pushvalue(L, -2); /* 3->5 */ 574 | lua_pushvalue(L, -2); /* 4->6 */ 575 | /* stack: table [_LOADED name prefix.name ret prefix.name ret] */ 576 | lua_rawset(L, -6); /* 4,5->1 */ 577 | } 578 | lua_remove(L, -2); /* (3) */ 579 | lua_rawset(L, -4); /* 2,3->table */ 580 | } 581 | lua_pop(L, 1); 582 | } 583 | 584 | 585 | /* lbind utils functions */ 586 | 587 | static int lbL_traceback(lua_State *L) { 588 | const char *msg = lua_tostring(L, 1); 589 | if (msg) 590 | luaL_traceback(L, L, msg, 1); 591 | else if (!lua_isnoneornil(L, 1)) { /* is there an error object? */ 592 | if (!luaL_callmeta(L, 1, "__tostring")) /* try its 'tostring' metamethod */ 593 | lua_pushliteral(L, "(no error message)"); 594 | } 595 | return 1; 596 | } 597 | 598 | LB_API int lbind_relindex(int idx, int onstack) { 599 | return (idx > 0 || idx <= LUA_REGISTRYINDEX) 600 | ? idx 601 | : idx - onstack; 602 | } 603 | 604 | LB_API int lbind_argferror(lua_State *L, int idx, const char *fmt, ...) { 605 | const char *errmsg; 606 | va_list argp; 607 | va_start(argp, fmt); 608 | errmsg = lua_pushvfstring(L, fmt, argp); 609 | va_end(argp); 610 | return luaL_argerror(L, idx, errmsg); 611 | } 612 | 613 | LB_API int lbind_typeerror(lua_State *L, int idx, const char *tname) { 614 | const char *real_type = lbind_type(L, idx); 615 | return lbind_argferror(L, idx, "%s expected, got %s", tname, 616 | real_type != NULL ? real_type : luaL_typename(L, idx)); 617 | } 618 | 619 | LB_API int lbind_matcherror(lua_State *L, const char *extramsg) { 620 | lua_Debug ar; 621 | lua_getinfo(L, "n", &ar); 622 | if (ar.name == NULL) 623 | ar.name = "?"; 624 | return luaL_error(L, "no matching functions for call to %s\n" 625 | "candidates are:\n%s", ar.name, extramsg); 626 | } 627 | 628 | LB_API int lbind_copystack(lua_State *from, lua_State *to, int n) { 629 | int i; 630 | luaL_checkstack(from, n, "too many args"); 631 | for (i = 0; i < n; ++i) 632 | lua_pushvalue(from, -n); 633 | lua_xmove(from, to, n); 634 | return n; 635 | } 636 | 637 | LB_API const char *lbind_dumpstack(lua_State *L, const char *msg) { 638 | int i, top = lua_gettop(L); 639 | luaL_Buffer b; 640 | luaL_buffinit(L, &b); 641 | luaL_addstring(&b, "dump stack: "); 642 | luaL_addstring(&b, msg != NULL ? msg : ""); 643 | luaL_addstring(&b, "\n---------------------------\n"); 644 | for (i = 1; i <= top; ++i) { 645 | lua_pushfstring(L, "%d: ", i); 646 | luaL_addvalue(&b); 647 | lbind_tolstring(L, i, NULL); 648 | luaL_addvalue(&b); 649 | luaL_addstring(&b, "\n"); 650 | } 651 | luaL_addstring(&b, "---------------------------\n"); 652 | luaL_pushresult(&b); 653 | return lua_tostring(L, -1); 654 | } 655 | 656 | LB_API int lbind_hasfield(lua_State *L, int idx, const char *field) { 657 | int hasfield = lua53_getfield(L, idx, field) != LUA_TNIL; 658 | lua_pop(L, 1); 659 | return hasfield; 660 | } 661 | 662 | LB_API int lbind_self(lua_State *L, const void *p, const char *method, int nargs, int *ptraceback) { 663 | luaL_checkstack(L, nargs+3, "too many arguments to self call"); 664 | if (!lbind_retrieve(L, p)) return 0; /* 1 */ 665 | if (lua53_getfield(L, -1, method) == LUA_TNIL) { /* 2 */ 666 | lua_pop(L, 2); 667 | return 0; 668 | } 669 | if (ptraceback) { 670 | lua_pushcfunction(L, lbL_traceback); 671 | lua_insert(L, -3); 672 | *ptraceback = lua_gettop(L) - 3; 673 | } 674 | lua_insert(L, -2); 675 | /* stack: traceback method object */ 676 | return 1; 677 | } 678 | 679 | LB_API int lbind_pcall(lua_State *L, int nargs, int nrets) { 680 | int res, tb_idx; 681 | lua_pushcfunction(L, lbL_traceback); 682 | lua_insert(L, -nargs-2); 683 | tb_idx = lua_gettop(L)-nargs-1; 684 | res = lua_pcall(L, nargs, nrets, tb_idx); 685 | lua_remove(L, tb_idx); 686 | return res; 687 | } 688 | 689 | 690 | /* metatable utils */ 691 | 692 | static int lbL_libcall(lua_State *L) { 693 | lua_pushvalue(L, lua_upvalueindex(1)); 694 | if (lua53_rawget(L, 1) == LUA_TNIL) { 695 | lua_pushfstring(L, "no such method (%s)", lua_tostring(L, lua_upvalueindex(1))); 696 | return luaL_argerror(L, 1, lua_tostring(L, -1)); 697 | } 698 | lua_replace(L, 1); 699 | lua_call(L, lua_gettop(L)-1, LUA_MULTRET); 700 | return lua_gettop(L); 701 | } 702 | 703 | static int lbM_callacc(lua_State *L, int idx, int nargs) { 704 | lua_CFunction f = lua_tocfunction(L, idx); 705 | if (f != NULL) { 706 | lua_settop(L, nargs); 707 | return f(L); 708 | } 709 | return -1; 710 | } 711 | 712 | static int lbM_calllut(lua_State *L, int idx, int nargs) { 713 | lua_CFunction f = lua_tocfunction(L, idx); 714 | /* look up table */ 715 | if (f == NULL) { 716 | lua_pushvalue(L, 2); 717 | lua_rawget(L, lbind_relindex(idx, 1)); 718 | f = lua_tocfunction(L, -1); 719 | } 720 | if (f != NULL) { 721 | lua_settop(L, nargs); 722 | return f(L); 723 | } 724 | return -1; 725 | } 726 | 727 | static int lbL_newindex(lua_State *L) { 728 | int nret; 729 | /* upvalue: seti, seth 730 | * order: 731 | * - lut 732 | * - accessor 733 | * - normaltable 734 | * - uservalue 735 | */ 736 | if (!lua_isnone(L, lua_upvalueindex(1)) && 737 | (nret = lbM_calllut(L, lua_upvalueindex(1), 3)) >= 0) 738 | return nret; 739 | if (!lua_isnone(L, lua_upvalueindex(2)) && 740 | (nret = lbM_callacc(L, lua_upvalueindex(2), 3)) >= 0) 741 | return nret; 742 | if (!lua_isuserdata(L, 1)) { 743 | lua_settop(L, 3); 744 | lua_rawset(L, 1); 745 | return 0; 746 | } 747 | if (lua53_getuservalue(L, 1) == LUA_TNIL) { 748 | lua_pop(L, 1); 749 | lua_newtable(L); 750 | lua_pushvalue(L, -1); 751 | lua_setuservalue(L, 1); 752 | } 753 | lua_pushvalue(L, 2); 754 | lua_pushvalue(L, 3); 755 | lua_rawset(L, -3); 756 | return 0; 757 | } 758 | 759 | static int lbL_index(lua_State *L) { 760 | int i, nret; 761 | /* upvalue: geti, geth, tables 762 | * order: 763 | * - uservalue 764 | * - metatable 765 | * - lut 766 | * - accessor 767 | * - upvalue tables 768 | */ 769 | if (lua_isuserdata(L, 1)) { 770 | if (lua53_getuservalue(L, 1) != LUA_TNIL) { 771 | lua_pushvalue(L, 2); 772 | if (lua53_rawget(L, -2) != LUA_TNIL) 773 | return 1; 774 | } 775 | } 776 | if (lua_getmetatable(L, 1)) { 777 | lua_pushvalue(L, 2); 778 | if (lua53_rawget(L, -2) != LUA_TNIL) 779 | return 1; 780 | } 781 | if (!lua_isnone(L, lua_upvalueindex(1)) && 782 | (nret = lbM_calllut(L, lua_upvalueindex(1), 2)) >= 0) 783 | return nret; 784 | if (!lua_isnone(L, lua_upvalueindex(2)) && 785 | (nret = lbM_callacc(L, lua_upvalueindex(2), 2)) >= 0) 786 | return nret; 787 | /* find in libtable/superlibtable */ 788 | for (i = 3; !lua_isnone(L, lua_upvalueindex(i)); ++i) { 789 | lua_settop(L, 2); 790 | if (lua_islightuserdata(L, lua_upvalueindex(i))) { 791 | if (!lbind_getmetatable(L, lua_touserdata(L, lua_upvalueindex(i)))) 792 | continue; 793 | lua_replace(L, lua_upvalueindex(i)); 794 | } 795 | lua_pushvalue(L, 2); 796 | if (lua53_gettable(L, lua_upvalueindex(i)) != LUA_TNIL) 797 | return 1; 798 | } 799 | return 0; 800 | } 801 | 802 | static void lbM_newindex(lua_State *L) { 803 | lua_pushnil(L); 804 | lua_pushnil(L); 805 | lua_pushcclosure(L, lbL_newindex, 2); 806 | } 807 | 808 | static void lbM_index(lua_State *L, int ntables) { 809 | lua_pushnil(L); 810 | lua_pushnil(L); 811 | if (ntables != 0) 812 | lua53_rotate(L, -ntables-2, 2); 813 | lua_pushcclosure(L, lbL_index, ntables+2); 814 | } 815 | 816 | static void get_default_metafield(lua_State *L, int idx, int field) { 817 | if (field == LBIND_INDEX) { 818 | if (lua53_getfield(L, idx, "__index") == LUA_TNIL 819 | || lua_tocfunction(L, -1) != lbL_index) { 820 | lua_pop(L, 1); 821 | lbM_index(L, 0); 822 | lua_pushvalue(L, -1); 823 | lua_setfield(L, lbind_relindex(idx, 2), "__index"); 824 | } 825 | } 826 | else if (field == LBIND_NEWINDEX) { 827 | if (lua53_getfield(L, idx, "__newindex") == LUA_TNIL 828 | || lua_tocfunction(L, -1) != lbL_newindex) { 829 | lua_pop(L, 1); 830 | lbM_newindex(L); 831 | lua_pushvalue(L, -1); 832 | lua_setfield(L, lbind_relindex(idx, 2), "__newindex"); 833 | } 834 | } 835 | } 836 | 837 | static void set_cfuncupvalue(lua_State *L, lua_CFunction f, int field, int idx) { 838 | if ((field & LBIND_INDEX) != 0) { 839 | get_default_metafield(L, -1, LBIND_INDEX); 840 | lua_pushcfunction(L, f); 841 | lua_setupvalue(L, -2, idx); 842 | lua_pop(L, 1); 843 | } 844 | if ((field & LBIND_NEWINDEX) != 0) { 845 | get_default_metafield(L, -1, LBIND_NEWINDEX); 846 | lua_pushcfunction(L, f); 847 | lua_setupvalue(L, -2, idx); 848 | lua_pop(L, 1); 849 | } 850 | } 851 | 852 | LB_API int lbind_setmetatable(lua_State *L, const void *t) { 853 | if (lbind_getmetatable(L, t)) { 854 | lua_setmetatable(L, -2); 855 | return 1; 856 | } 857 | return 0; 858 | } 859 | 860 | LB_API int lbind_getmetatable(lua_State *L, const void *t) { 861 | if (lua53_rawgetp(L, LUA_REGISTRYINDEX, t) == LUA_TNIL) { 862 | lua_pop(L, 1); 863 | return 0; 864 | } 865 | return 1; 866 | } 867 | 868 | LB_API int lbind_setmetafield(lua_State *L, int idx, const char *field) { 869 | if (!lua_getmetatable(L, idx)) { 870 | lua_createtable(L, 0, 1); 871 | lua_pushvalue(L, -2); 872 | lua_setfield(L, -2, field); 873 | lua_setmetatable(L, lbind_relindex(idx, 1)); 874 | lua_pop(L, 1); 875 | return 1; 876 | } 877 | lua_pushvalue(L, -2); 878 | lua_setfield(L, -2, field); 879 | lua_pop(L, 2); 880 | return 0; 881 | } 882 | 883 | LB_API int lbind_setlibcall(lua_State *L, const char *method) { 884 | if (method == NULL) method = "new"; 885 | lua_pushstring(L, method); 886 | lua_pushcclosure(L, lbL_libcall, 1); 887 | return lbind_setmetafield(L, -2, "__call"); 888 | } 889 | 890 | LB_API void lbind_setaccessors(lua_State *L, int ntables, int field) { 891 | if ((field & LBIND_INDEX) != 0) { 892 | lua_pushnil(L); 893 | lua_pushnil(L); 894 | if (ntables > 0) 895 | lua53_rotate(L, -ntables-2, 2); 896 | lua_pushcclosure(L, lbL_index, ntables+2); 897 | lua_setfield(L, -2, "__index"); 898 | } 899 | if ((field & LBIND_NEWINDEX) != 0) { 900 | lua_pushnil(L); 901 | lua_pushnil(L); 902 | lua_pushcclosure(L, lbL_newindex, 2); 903 | lua_setfield(L, -2, "__newindex"); 904 | } 905 | } 906 | 907 | LB_API void lbind_sethashf(lua_State *L, lua_CFunction f, int field) { 908 | set_cfuncupvalue(L, f, field, 1); 909 | } 910 | 911 | LB_API void lbind_setarrayf(lua_State *L, lua_CFunction f, int field) { 912 | set_cfuncupvalue(L, f, field, 2); 913 | } 914 | 915 | LB_API void lbind_setmaptable(lua_State *L, luaL_Reg libs[], int field) { 916 | lua_newtable(L); 917 | luaL_setfuncs(L, libs, 0); 918 | if ((field & LBIND_INDEX) != 0) { 919 | get_default_metafield(L, -2, LBIND_INDEX); 920 | lua_pushvalue(L, -2); 921 | lua_setupvalue(L, -2, 1); 922 | lua_pop(L, 1); 923 | } 924 | if ((field & LBIND_NEWINDEX) != 0) { 925 | get_default_metafield(L, -2, LBIND_NEWINDEX); 926 | lua_pushvalue(L, -2); 927 | lua_setupvalue(L, -2, 1); 928 | lua_pop(L, 1); 929 | } 930 | lua_pop(L, 1); 931 | } 932 | 933 | 934 | /* lbind userdata maintain */ 935 | 936 | typedef union { 937 | lbind_MaxAlign dummy; /* ensures maximum alignment for `intern' object */ 938 | struct { 939 | void *instance; 940 | int flags; 941 | } o; 942 | } lbind_Object; 943 | 944 | #define check_size(L,n) (lua_rawlen((L),(n)) >= sizeof(lbind_Object)) 945 | 946 | static lbind_Object *lbO_new(lua_State *L, size_t objsize, int flags) { 947 | lbind_Object *obj; 948 | obj = (lbind_Object*)lua_newuserdata(L, sizeof(lbind_Object) + objsize); 949 | obj->o.flags = flags; 950 | obj->o.instance = (void*)(obj+1); 951 | if (objsize != 0 && (flags & LBIND_INTERN) != 0) 952 | lbind_intern(L, obj->o.instance); 953 | return obj; 954 | } 955 | 956 | static lbind_Object *lbO_test(lua_State *L, int idx) { 957 | lbind_Object *obj = (lbind_Object*)lbind_touserdata(L, idx); 958 | if (obj != NULL) { 959 | if (!check_size(L, idx) || obj->o.instance == NULL) 960 | obj = NULL; 961 | #if 0 962 | else { 963 | lbB_internbox(L); /* 1 */ 964 | lua_rawgetp(L, -1, obj->o.instance); /* 2 */ 965 | if (!lua_rawequal(L, lbind_relindex(idx, 2), -1)) 966 | obj = NULL; 967 | lua_pop(L, 2); /* (2)(1) */ 968 | } 969 | #endif 970 | } 971 | return obj; 972 | } 973 | 974 | LB_API void *lbind_touserdata(lua_State *L, int idx) { 975 | #ifndef LBIND_NO_PEER 976 | if (lua_istable(L, idx)) { 977 | if (lua53_getfield(L, idx, "__peer") == LUA_TNIL) { 978 | lua_pop(L, 1); 979 | return NULL; 980 | } 981 | lua_replace(L, idx); 982 | } 983 | #endif 984 | return lua_touserdata(L, idx); 985 | } 986 | 987 | LB_API void *lbind_raw(lua_State *L, size_t objsize, int intern) { 988 | return lbO_new(L, objsize, intern ? LBIND_INTERN : 0)->o.instance; 989 | } 990 | 991 | LB_API void *lbind_new(lua_State *L, size_t objsize, const lbind_Type *t) { 992 | lbind_Object *obj = lbO_new(L, objsize, t->flags); 993 | if (lbind_getmetatable(L, t)) 994 | lua_setmetatable(L, -2); 995 | return obj->o.instance; 996 | } 997 | 998 | LB_API void *lbind_wrap(lua_State *L, void *p, const lbind_Type *t) { 999 | lbind_Object *obj = lbO_new(L, 0, t->flags); 1000 | obj->o.instance = p; 1001 | if ((obj->o.flags & LBIND_INTERN) != 0) 1002 | lbind_intern(L, p); 1003 | if (lbind_getmetatable(L, t)) 1004 | lua_setmetatable(L, -2); 1005 | return p; 1006 | } 1007 | 1008 | LB_API void *lbind_delete(lua_State *L, int idx) { 1009 | void *u = NULL; 1010 | lbind_Object *obj = (lbind_Object*)lbind_touserdata(L, idx); 1011 | if (obj != NULL) { 1012 | if (!check_size(L, idx)) 1013 | return NULL; 1014 | if ((u = obj->o.instance) != NULL) { 1015 | obj->o.instance = NULL; 1016 | obj->o.flags &= ~LBIND_TRACK; 1017 | #if LUA_VERSION_NUM < 502 1018 | lbB_internbox(L); /* 1 */ 1019 | lua_pushnil(L); /* 2 */ 1020 | lua_rawsetp(L, -3, u); /* 2->1 */ 1021 | lua_pop(L, 1); /* (1) */ 1022 | #endif 1023 | } 1024 | } 1025 | return u; 1026 | } 1027 | 1028 | LB_API void *lbind_object(lua_State *L, int idx) { 1029 | lbind_Object *obj = lbO_test(L, idx); 1030 | return obj == NULL ? NULL : obj->o.instance; 1031 | } 1032 | 1033 | LB_API void lbind_intern(lua_State *L, const void *p) { 1034 | /* stack: object */ 1035 | lbB_internbox(L); 1036 | lua_pushvalue(L, -2); 1037 | lua_rawsetp(L, -2, p); 1038 | lua_pop(L, 1); 1039 | } 1040 | 1041 | LB_API int lbind_retrieve(lua_State *L, const void *p) { 1042 | if (p == NULL) return 0; 1043 | lbB_internbox(L); /* 1 */ 1044 | if (lua53_rawgetp(L, -1, p) == LUA_TNIL) { /* 2 */ 1045 | lua_pop(L, 2); 1046 | return 0; 1047 | } 1048 | lua_remove(L, -2); 1049 | return 1; 1050 | } 1051 | 1052 | LB_API void lbind_track(lua_State *L, int idx) { 1053 | lbind_Object *obj = lbO_test(L, idx); 1054 | if (obj != NULL) 1055 | obj->o.flags |= LBIND_TRACK; 1056 | } 1057 | 1058 | LB_API void lbind_untrack(lua_State *L, int idx) { 1059 | lbind_Object *obj = lbO_test(L, idx); 1060 | if (obj != NULL) 1061 | obj->o.flags &= ~LBIND_TRACK; 1062 | } 1063 | 1064 | LB_API int lbind_hastrack(lua_State *L, int idx) { 1065 | lbind_Object *obj = lbO_test(L, idx); 1066 | return obj != NULL && (obj->o.flags & LBIND_TRACK) != 0; 1067 | } 1068 | 1069 | 1070 | /* lbind type registry */ 1071 | 1072 | LB_API void lbind_inittype(lbind_Type *t, const char *name) { 1073 | t->name = name; 1074 | t->flags = LBIND_DEFAULT_FLAG; 1075 | t->cast = NULL; 1076 | t->bases = NULL; 1077 | } 1078 | 1079 | LB_API void lbind_setbase(lbind_Type *t, lbind_Type **bases, lbind_Cast *cast) { 1080 | t->bases = bases; 1081 | t->cast = cast; 1082 | if (bases != NULL) 1083 | t->flags &= LBIND_ACCESSOR; 1084 | } 1085 | 1086 | LB_API int lbind_settrack(lbind_Type *t, int autotrack) { 1087 | int old_flag = t->flags&LBIND_TRACK ? 1 : 0; 1088 | if (autotrack) 1089 | t->flags |= LBIND_TRACK; 1090 | else 1091 | t->flags &= ~LBIND_TRACK; 1092 | return old_flag; 1093 | } 1094 | 1095 | LB_API int lbind_setintern(lbind_Type *t, int autointern) { 1096 | int old_flag = t->flags&LBIND_INTERN ? 1 : 0; 1097 | if (autointern) 1098 | t->flags |= LBIND_INTERN; 1099 | else 1100 | t->flags &= ~LBIND_INTERN; 1101 | return old_flag; 1102 | } 1103 | 1104 | LB_API lbind_Type *lbind_typeobject(lua_State *L, int idx) { 1105 | lbind_Type *t = NULL; 1106 | if (lua_getmetatable(L, idx)) { 1107 | lua_getfield(L, -1, "__type"); 1108 | t = (lbind_Type*)lua_touserdata(L, -1); 1109 | lua_pop(L, 2); 1110 | if (t != NULL) 1111 | return t; 1112 | } 1113 | if (lua_istable(L, idx)) { 1114 | lua_getfield(L, idx, "__type"); 1115 | t = (lbind_Type*)lua_touserdata(L, -1); 1116 | lua_pop(L, 1); 1117 | } 1118 | return t; 1119 | } 1120 | 1121 | 1122 | /* lbind type metatable */ 1123 | 1124 | static int lbL_tostring(lua_State *L) { 1125 | lbind_tolstring(L, 1, NULL); 1126 | return 1; 1127 | } 1128 | 1129 | static int lbL_gc(lua_State *L) { 1130 | lbind_Object *obj = (lbind_Object*)lua_touserdata(L, 1); 1131 | if (obj != NULL && check_size(L, 1)) { 1132 | if ((obj->o.flags & LBIND_TRACK) != 0) { 1133 | if (lua53_getfield(L, 1, "delete") != LUA_TNIL) { 1134 | lua_pushvalue(L, 1); 1135 | lua_call(L, 1, 0); 1136 | } 1137 | if ((obj->o.flags & LBIND_TRACK) != 0) 1138 | lbind_delete(L, 1); 1139 | } 1140 | } 1141 | return 0; 1142 | } 1143 | 1144 | static void lbT_register(lua_State *L, const char *name, const void *t) { 1145 | /* stack: metatable */ 1146 | lua_pushvalue(L, -1); 1147 | lua_setfield(L, LUA_REGISTRYINDEX, name); 1148 | lua_pushvalue(L, -1); 1149 | lua_rawsetp(L, LUA_REGISTRYINDEX, t); 1150 | 1151 | lbB_typebox(L); 1152 | lua_pushvalue(L, -2); 1153 | lua_setfield(L, -2, name); 1154 | lua_pushvalue(L, -2); 1155 | lua_rawsetp(L, -2, t); 1156 | lua_pop(L, 1); 1157 | } 1158 | 1159 | static int lbT_exists(lua_State *L, const lbind_Type *t) { 1160 | if (lua53_rawgetp(L, LUA_REGISTRYINDEX, (const void*)t) != LUA_TNIL) { 1161 | lua_pop(L, 1); 1162 | return 1; 1163 | } 1164 | 1165 | if (lua53_getfield(L, LUA_REGISTRYINDEX, t->name) != LUA_TNIL) { 1166 | lua_pop(L, 1); 1167 | return 1; 1168 | } 1169 | 1170 | lua_pop(L, 2); 1171 | return 0; 1172 | } 1173 | 1174 | static int lbL_agency(lua_State *L) { 1175 | lua_pushvalue(L, lua_upvalueindex(1)); 1176 | lua_gettable(L, 1); 1177 | lua_insert(L, 1); 1178 | lua_call(L, lua_gettop(L)-1, LUA_MULTRET); 1179 | return lua_gettop(L); 1180 | } 1181 | 1182 | static void lbT_setagency(lua_State *L, const char *key) { 1183 | /* stack: libtable mt */ 1184 | lua_pushstring(L, key); 1185 | if (lua53_rawget(L, -3) != LUA_TNIL) { /* do not track __index */ 1186 | lua_pushfstring(L, "__%s", key); 1187 | lua_pushstring(L, key); 1188 | lua_pushcclosure(L, lbL_agency, 1); 1189 | lua_rawset(L, -4); 1190 | } 1191 | lua_pop(L, 1); 1192 | } 1193 | 1194 | LB_API int lbind_newmetatable(lua_State *L, luaL_Reg *libs, const lbind_Type *t) { 1195 | if (lbT_exists(L, t)) return 0; 1196 | 1197 | lua_createtable(L, 0, 8); 1198 | if (libs != NULL) 1199 | luaL_setfuncs(L, libs, 0); 1200 | 1201 | /* init type metatable */ 1202 | lua_pushlightuserdata(L, (void*)t); 1203 | lua_setfield(L, -2, "__type"); 1204 | 1205 | if (!lbind_hasfield(L, -1, "__gc")) { 1206 | lua_pushcfunction(L, lbL_gc); 1207 | lua_setfield(L, -2, "__gc"); 1208 | } 1209 | 1210 | if (!lbind_hasfield(L, -1, "__tostring")) { 1211 | lua_pushcfunction(L, lbL_tostring); 1212 | lua_setfield(L, -2, "__tostring"); 1213 | } 1214 | 1215 | if ((t->flags & LBIND_ACCESSOR) != 0) { 1216 | int nups = 0; 1217 | int freeslots = 0; 1218 | lbind_Type **bases = t->bases; 1219 | if (bases != NULL) { 1220 | for (; *bases != NULL; ++nups, ++bases) { 1221 | if (nups > freeslots) { 1222 | luaL_checkstack(L, 10, "no space for base types"); 1223 | freeslots += 10; 1224 | } 1225 | if (!lbind_getmetatable(L, *bases)) 1226 | lua_pushlightuserdata(L, *bases); 1227 | } 1228 | } 1229 | lbind_setaccessors(L, nups, LBIND_INDEX|LBIND_NEWINDEX); 1230 | } 1231 | 1232 | else if (!lbind_hasfield(L, -1, "__index")) { 1233 | lua_pushvalue(L, -1); 1234 | lua_setfield(L, -2, "__index"); 1235 | } 1236 | 1237 | lbT_register(L, t->name, (const void*)t); 1238 | return 1; 1239 | } 1240 | 1241 | LB_API void lbind_setagency(lua_State *L) { 1242 | lbT_setagency(L, "len"); 1243 | #if LUA_VERSION_NUM >= 502 1244 | lbT_setagency(L, "pairs"); 1245 | lbT_setagency(L, "ipairs"); 1246 | #endif /* LUA_VERSION_NUM >= 502 */ 1247 | } 1248 | 1249 | 1250 | /* lbind type system */ 1251 | 1252 | static int lbT_testmeta(lua_State *L, int idx, const lbind_Type *t) { 1253 | if (lua_getmetatable(L, idx)) { /* does it have a metatable? */ 1254 | int res = 1; 1255 | if (!lbind_getmetatable(L, t)) { /* get correct metatable */ 1256 | lua_pop(L, 1); /* no such metatable? fail */ 1257 | return 0; 1258 | } 1259 | if (!lua_rawequal(L, -1, -2)) /* not the same? */ 1260 | res = 0; /* value is a userdata with wrong metatable */ 1261 | lua_pop(L, 2); 1262 | return res; 1263 | } 1264 | return 0; 1265 | } 1266 | 1267 | static void *lbT_trycast(lua_State *L, int idx, const lbind_Type *t) { 1268 | lbind_Type *from_type = lbind_typeobject(L, idx); 1269 | void *obj = NULL; 1270 | if (from_type != NULL 1271 | && from_type->cast != NULL 1272 | && (obj = from_type->cast(L, idx, t)) != NULL) 1273 | return obj; 1274 | if (t->cast != NULL && (obj = t->cast(L, idx, from_type)) != NULL) 1275 | return obj; 1276 | return NULL; 1277 | } 1278 | 1279 | LB_API const char *lbind_tolstring(lua_State *L, int idx, size_t *plen) { 1280 | const char *tname = lbind_type(L, idx); 1281 | lbind_Object *obj = lbO_test(L, idx); 1282 | if (obj != NULL && tname) 1283 | lua_pushfstring(L, "%s: %p", tname, obj->o.instance); 1284 | else if (obj == NULL) { 1285 | lbind_Object *obj = (lbind_Object*)lbind_touserdata(L, idx); 1286 | if (obj == NULL) 1287 | return luaL_tolstring(L, idx, plen); 1288 | if (tname && check_size(L, idx)) 1289 | lua_pushfstring(L, "%s[N]: %p", tname, obj->o.instance); 1290 | else 1291 | lua_pushfstring(L, "userdata: %p", (void*)obj); 1292 | } 1293 | return lua_tolstring(L, -1, plen); 1294 | } 1295 | 1296 | LB_API const char *lbind_type(lua_State *L, int idx) { 1297 | lbind_Type *t = lbind_typeobject(L, idx); 1298 | if (t != NULL) return t->name; 1299 | return NULL; 1300 | } 1301 | 1302 | LB_API int lbind_isa(lua_State *L, int idx, const lbind_Type *t) { 1303 | return lbT_testmeta(L, idx, t) || lbT_trycast(L, idx, t) != NULL; 1304 | } 1305 | 1306 | LB_API void *lbind_cast(lua_State *L, int idx, const lbind_Type *t) { 1307 | lbind_Object *obj = (lbind_Object*)lbind_touserdata(L, idx); 1308 | if (!check_size(L, idx) || obj == NULL || obj->o.instance == NULL) 1309 | return NULL; 1310 | return lbT_testmeta(L, idx, t) ? obj->o.instance : lbT_trycast(L, idx, t); 1311 | } 1312 | 1313 | LB_API int lbind_copy(lua_State *L, const void *obj, const lbind_Type *t) { 1314 | if (!lbind_getmetatable(L, t)) /* 1 */ 1315 | return 0; 1316 | lua_pushliteral(L, "new"); /* 2 */ 1317 | if (lua53_rawget(L, -2) == LUA_TNIL) { /* 2->2 */ 1318 | lua_pop(L, 2); /* (2)(1) */ 1319 | return 0; 1320 | } 1321 | lua_remove(L, -2); /* (1) */ 1322 | if (!lbind_retrieve(L, obj)) 1323 | lbind_wrap(L, (void*)obj, t); 1324 | if (lua_pcall(L, 1, 1, 0) != LUA_OK) { 1325 | lua_pop(L, 1); 1326 | return 0; 1327 | } 1328 | lbind_track(L, -1); /* enable autodeletion for copied stuff */ 1329 | return 1; 1330 | } 1331 | 1332 | LB_API void *lbind_check(lua_State *L, int idx, const lbind_Type *t) { 1333 | lbind_Object *obj = (lbind_Object*)lbind_touserdata(L, idx); 1334 | void *u = NULL; 1335 | if (!check_size(L, idx)) 1336 | luaL_argerror(L, idx, "invalid lbind userdata"); 1337 | if (obj == NULL || obj->o.instance == NULL) { 1338 | luaL_argerror(L, idx, "null lbind object"); 1339 | return NULL; 1340 | } 1341 | u = lbT_testmeta(L, idx, t) ? obj->o.instance : lbT_trycast(L, idx, t); 1342 | if (u == NULL) lbind_typeerror(L, idx, t->name); 1343 | return u; 1344 | } 1345 | 1346 | LB_API void *lbind_test(lua_State *L, int idx, const lbind_Type *t) { 1347 | lbind_Object *obj = (lbind_Object*)lbind_touserdata(L, idx); 1348 | return lbT_testmeta(L, idx, t) ? obj->o.instance : lbT_trycast(L, idx, t); 1349 | } 1350 | 1351 | 1352 | /* lbind enum/mask support */ 1353 | #ifndef LBIND_NO_ENUM 1354 | static const char *lbE_skipwhite(const char *s) { 1355 | while (*s == '\t' || *s == '\n' || *s == '\r' 1356 | || *s == ' ' || *s == '+' || *s == '|' || *s == ',') 1357 | ++s; 1358 | return s; 1359 | } 1360 | 1361 | static const char *lbE_skipident(const char *s) { 1362 | while ((*s >= 'A' && *s <= 'Z') || 1363 | (*s >= 'a' && *s <= 'z') || 1364 | (*s == '-' || *s == '_')) 1365 | ++s; 1366 | return s; 1367 | } 1368 | 1369 | static int lbE_parsemask(lbind_Enum *et, const char *s, int *penum, lua_State *L) { 1370 | *penum = 0; 1371 | while (*s != '\0') { 1372 | const char *e; 1373 | int inversion = 0; 1374 | lbind_EnumItem *item; 1375 | s = lbE_skipwhite(s); 1376 | if (*s == '~') { 1377 | inversion = 1; 1378 | s = lbE_skipwhite(s+1); 1379 | } 1380 | if (*s == '\0') break; 1381 | e = lbE_skipident(s); 1382 | if (e == s || (item = lbind_findenum(et, s, e-s)) == NULL) { 1383 | if (L == NULL) return 0; 1384 | if (e == s) 1385 | return luaL_error(L, "unexpected token '%c' in %s", *s, et->name); 1386 | else { 1387 | lua_pushlstring(L, s, e-s); 1388 | return luaL_error(L, "unexpected mask '%s' in %s", lua_tostring(L, -1), et->name); 1389 | } 1390 | } 1391 | s = e; 1392 | if (inversion) 1393 | *penum &= ~item->value; 1394 | else 1395 | *penum |= item->value; 1396 | } 1397 | return 1; 1398 | } 1399 | 1400 | static int lbE_icmp(int ch1, int ch2) { 1401 | if (ch1 == ch2) 1402 | return 1; 1403 | if (ch1 >= 'A' && ch1 <= 'Z') 1404 | ch1 += 'a' - 'A'; 1405 | if (ch2 >= 'A' && ch2 <= 'Z') 1406 | ch1 += 'a' - 'A'; 1407 | return ch1 == ch2; 1408 | } 1409 | 1410 | static int lbE_stricmp(const char *a, const char *b, size_t len) { 1411 | size_t i; 1412 | for (i = 0; i < len; ++i, ++a, ++b) { 1413 | if (!lbE_icmp(*a, *b)) 1414 | return *a > *b ? 1 : -1; 1415 | if (*a == '\0') 1416 | return *b == '\0' ? 0 : -1; 1417 | if (*b == '\0') 1418 | return *a != '\0' ? 1 : 0; 1419 | } 1420 | return 0; 1421 | } 1422 | 1423 | static int lbE_toenum(lua_State *L, int idx, lbind_Enum *et, int mask, int check) { 1424 | int type = lua_type(L, idx); 1425 | if (type == LUA_TNUMBER) 1426 | return (int)lua_tointeger(L, idx); 1427 | else if (type == LUA_TSTRING) { 1428 | size_t len; 1429 | const char *s = lua_tolstring(L, idx, &len); 1430 | int value; 1431 | if (!mask) { 1432 | lbind_EnumItem *item = lbind_findenum(et, s, len); 1433 | if (item == NULL && check) 1434 | return luaL_error(L, "invalid %s value: %s", et->name, s); 1435 | if (item != NULL) 1436 | return item->value; 1437 | } 1438 | else if (lbE_parsemask(et, s, &value, check ? L : NULL)) 1439 | return value; 1440 | } 1441 | if (check) 1442 | lbind_typeerror(L, idx, et->name); 1443 | return -1; 1444 | } 1445 | 1446 | LB_API void lbind_initenum(lbind_Enum *et, const char *name) { 1447 | et->name = name; 1448 | et->nitem = 0; 1449 | et->items = NULL; 1450 | } 1451 | 1452 | LB_API lbind_EnumItem *lbind_findenum(lbind_Enum *et, const char *s, size_t len) { 1453 | size_t b = 0, e = et->nitem-1; 1454 | while (b < e) { 1455 | size_t mid = (b + e) >> 1; 1456 | int res = lbE_stricmp(et->items[mid].name, s, len); 1457 | if (res == 0) 1458 | return &et->items[mid]; 1459 | else if (res < 0) 1460 | b = mid + 1; 1461 | else 1462 | e = mid; 1463 | } 1464 | return NULL; 1465 | } 1466 | 1467 | LB_API int lbind_pushmask(lua_State *L, int value, lbind_Enum *et) { 1468 | luaL_Buffer b; 1469 | lbind_EnumItem *items; 1470 | int first = 1; 1471 | if (et->items == NULL) { 1472 | lua_pushliteral(L, ""); 1473 | return 0; 1474 | } 1475 | luaL_buffinit(L, &b); 1476 | for (items = et->items; items->name != NULL; ++items) { 1477 | if ((items->value & value) == value) { 1478 | if (first) 1479 | first = 0; 1480 | else 1481 | luaL_addchar(&b, ' '); 1482 | luaL_addstring(&b, items->name); 1483 | value &= ~items->value; 1484 | } 1485 | } 1486 | luaL_pushresult(&b); 1487 | return 1; 1488 | } 1489 | 1490 | LB_API int lbind_pushenum(lua_State *L, const char *name, lbind_Enum *et) { 1491 | lbind_EnumItem *item = lbind_findenum(et, name, ~(size_t)0); 1492 | if (item == NULL) 1493 | return -1; 1494 | lua_pushinteger(L, item->value); 1495 | return item->value; 1496 | } 1497 | 1498 | LB_API int lbind_testmask(lua_State *L, int idx, lbind_Enum *et) { 1499 | return lbE_toenum(L, idx, et, 1, 0); 1500 | } 1501 | 1502 | LB_API int lbind_checkmask(lua_State *L, int idx, lbind_Enum *et) { 1503 | return lbE_toenum(L, idx, et, 1, 1); 1504 | } 1505 | 1506 | LB_API int lbind_testenum(lua_State *L, int idx, lbind_Enum *et) { 1507 | return lbE_toenum(L, idx, et, 0, 0); 1508 | } 1509 | 1510 | LB_API int lbind_checkenum(lua_State *L, int idx, lbind_Enum *et) { 1511 | return lbE_toenum(L, idx, et, 0, 1); 1512 | } 1513 | #endif /* LBIND_NO_ENUM */ 1514 | 1515 | 1516 | /* lbind Lua side runtime */ 1517 | #ifndef LBIND_NO_RUNTIME 1518 | static lbind_Type *lbT_test(lua_State *L, int idx) { 1519 | lbind_Type *t = (lbind_Type*)lua_touserdata(L, idx); 1520 | lbB_typebox(L); 1521 | lua_rawgetp(L, -1, t); 1522 | t = lua_touserdata(L, -1); 1523 | lua_pop(L, 2); 1524 | return t != NULL ? t : lbind_typeobject(L, -1); 1525 | } 1526 | 1527 | static int lbL_bases(lua_State *L) { 1528 | int i = 1; 1529 | lbind_Type **bases, *t = lbT_test(L, 1); 1530 | if (t == NULL) 1531 | return lbind_typeerror(L, 1, "type"); 1532 | bases = t->bases; 1533 | lua_settop(L, 2); 1534 | if (!lua_istable(L, 2)) { 1535 | lua_newtable(L); 1536 | lua_replace(L, 2); 1537 | } 1538 | for (; *bases != NULL; ++bases) { 1539 | if (!lbind_getmetatable(L, *bases)) 1540 | lua_pushnil(L); 1541 | lua_rawseti(L, -2, i); 1542 | } 1543 | lua_pushinteger(L, i); 1544 | lua_setfield(L, -2, "n"); 1545 | return 1; 1546 | } 1547 | 1548 | static int lbL_track(lua_State *L) { 1549 | int i, top = lua_gettop(L); 1550 | for (i = 1; i <= top; ++i) 1551 | lbind_track(L, i); 1552 | return top; 1553 | } 1554 | 1555 | static int lbL_untrack(lua_State *L) { 1556 | int i, top = lua_gettop(L); 1557 | for (i = 1; i <= top; ++i) 1558 | lbind_untrack(L, i); 1559 | return top; 1560 | } 1561 | 1562 | static int lbL_owner(lua_State *L) { 1563 | int i, top = lua_gettop(L); 1564 | luaL_checkstack(L, top, "no space for owner info"); 1565 | for (i = 1; i <= top; ++i) { 1566 | if (lbind_hastrack(L, i)) 1567 | lua_pushliteral(L, "Lua"); 1568 | else 1569 | lua_pushliteral(L, "C"); 1570 | } 1571 | return top; 1572 | } 1573 | 1574 | static int lbL_type(lua_State *L) { 1575 | int i, top = lua_gettop(L); 1576 | if (top == 0) { 1577 | lbB_typebox(L); 1578 | return 1; 1579 | } 1580 | for (i = 1; i <= top; ++i) { 1581 | lbind_Type *t = lbT_test(L, i); 1582 | lua_pushstring(L, t != NULL ? t->name : luaL_typename(L, -1)); 1583 | lua_replace(L, i); 1584 | } 1585 | return top; 1586 | } 1587 | 1588 | static int lbL_pointer(lua_State *L) { 1589 | int i, top = lua_gettop(L); 1590 | if (top == 0) { 1591 | lbB_internbox(L); 1592 | return 1; 1593 | } 1594 | for (i = 1; i <= top; ++i) { 1595 | const void *u = lbind_object(L, i); 1596 | if (u == NULL) 1597 | lua_pushnil(L); 1598 | else 1599 | lua_pushlightuserdata(L, (void*)u); 1600 | lua_replace(L, i); 1601 | } 1602 | return top; 1603 | } 1604 | 1605 | static int lbL_delete(lua_State *L) { 1606 | int i, top = lua_gettop(L); 1607 | for (i = 1; i <= top; ++i) { 1608 | if (lua53_getfield(L, i, "delete") != LUA_TNIL) { 1609 | lua_pushvalue(L, i); 1610 | lua_call(L, 1, 0); 1611 | } 1612 | else { 1613 | lua_pop(L, 1); 1614 | lbind_delete(L, i); 1615 | } 1616 | } 1617 | return 0; 1618 | } 1619 | 1620 | static int lbL_isa(lua_State *L) { 1621 | lbind_Type *t = lbT_test(L, 1); 1622 | int i, top = lua_gettop(L); 1623 | if (t == NULL) 1624 | lbind_typeerror(L, 1, "lbind object/type"); 1625 | for (i = 2; i <= top; ++i) { 1626 | if (!lbind_isa(L, i, t)) { 1627 | lua_pushnil(L); 1628 | lua_replace(L, i); 1629 | } 1630 | } 1631 | return top - 1; 1632 | } 1633 | 1634 | static int lbL_castto(lua_State *L) { 1635 | lbind_Type *t = lbT_test(L, 1); 1636 | int i, top = lua_gettop(L); 1637 | if (t == NULL) 1638 | lbind_typeerror(L, 1, "lbind object/type"); 1639 | for (i = 2; i <= top; ++i) { 1640 | void *u = lbind_cast(L, -2, t); 1641 | if (u == NULL) 1642 | lua_pushnil(L); 1643 | else if (!lbind_retrieve(L, u)) 1644 | lbind_wrap(L, u, t); 1645 | lua_replace(L, i); 1646 | } 1647 | return top - 1; 1648 | } 1649 | 1650 | LBLIB_API int luaopen_lbind(lua_State *L) { 1651 | luaL_Reg libs[] = { 1652 | #define ENTRY(name) { #name, lbL_##name } 1653 | ENTRY(bases), 1654 | ENTRY(castto), 1655 | ENTRY(delete), 1656 | ENTRY(isa), 1657 | ENTRY(owner), 1658 | ENTRY(pointer), 1659 | ENTRY(track), 1660 | ENTRY(type), 1661 | ENTRY(untrack), 1662 | #undef ENTRY 1663 | { NULL, NULL } 1664 | }; 1665 | 1666 | luaL_newlib(L, libs); 1667 | #if LUA_VERSION_NUM < 502 1668 | lua_pushvalue(L, -1); 1669 | lua_setglobal(L, "lbind"); 1670 | #endif 1671 | return 1; 1672 | } 1673 | #endif /* LBIND_NO_RUNTIME */ 1674 | 1675 | 1676 | LB_NS_END 1677 | 1678 | #endif /* LBIND_IMPLEMENTATION */ 1679 | /* vim: set sw=2: */ 1680 | /* win32cc: lua='lua53' flags+='-s -O2 -Wall -std=c99 -pedantic -mdll -Id:/$lua/include' 1681 | * win32cc: flags+='-DLBIND_IMPLEMENTATION -xc' output='lbind.dll' libs+='-l$lua' 1682 | * maccc: lua='lua53' flags+='-bundle -undefined dynamic_lookup -O2 -Wall -std=c99 -pedantic' 1683 | * maccc: flags+='-DLBIND_IMPLEMENTATION -xc' output='lbind.so' 1684 | * cc: run='$lua tt.lua' */ 1685 | -------------------------------------------------------------------------------- /test/gd.lbind.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ";../?/init.lua;../?.lua" 2 | require 'lbind'.export(_ENV) 3 | local types = require 'lbind.types'.export(_ENV) 4 | local C = types.class 5 | 6 | local t = module 'gd' { 7 | export = true, 8 | include "gd.h"; 9 | subfiles { 10 | --"gdImage.bind.lua"; 11 | }; 12 | 13 | object "gdImage" { 14 | method "new" :cname "gdImageCreate" 15 | (int "sx", int "sy") :rets(selfType:ptr()); 16 | method "newTrueColor" :cname "gdImageCreateTrueColor" 17 | (int "sx", int "sy") :rets(selfType:ptr()); 18 | method "delete" () :alias "close" :cname "gdImageDestroy"; 19 | method "color_allocate" :cname "gdImageColorAllocate" 20 | (int "r", int "g", int "b"); 21 | method "line" :cname "gdImageLine" 22 | (int "x1", int "y1", int "x2", int "y2", int "color"); 23 | 24 | include "errno.h"; 25 | include "string.h"; 26 | method "toPNG" (char:const():ptr "name") :body [[ 27 | FILE *pngout = fopen(name, "wb"); 28 | if (pngout == NULL) { 29 | lua_pushnil(L); 30 | lua_pushstring(L, strerror(errno)); 31 | return 2; 32 | } 33 | gdImagePng(self, pngout); 34 | fclose(pngout); 35 | lua_pushboolean(L, 1); 36 | return 1; 37 | ]]; 38 | }; 39 | }; 40 | 41 | local utils = require 'lbind.utils' 42 | --print(type(t)) 43 | print(utils.tostring(t)) 44 | -------------------------------------------------------------------------------- /tt.lua: -------------------------------------------------------------------------------- 1 | local exports = {} 2 | local _ENV = exports 3 | 4 | require 'lbind' .exports(_ENV) 5 | require 'lbind.typeinfo' .exports(_ENV) 6 | 7 | -- config module 8 | require 'lbind.config' { 9 | indent = 2; 10 | } 11 | 12 | module "test" 13 | :lang "C++" 14 | :output "test_gen.cpp" { 15 | 16 | include "stdio.h"; 17 | source [[ 18 | 19 | enum testEnum { 20 | enum1, 21 | enum2, 22 | enum3, 23 | enum4 = 98, 24 | enum5 = 98, 25 | }; 26 | 27 | class test { 28 | int x_; 29 | public: 30 | test(int x = 0) : x_(x) {} 31 | virtual int vx() { return x(); } 32 | int x() { return x_; } 33 | int proc(int(*f)(int)) { return f(x()); } 34 | }; 35 | 36 | int virt_test(test *t) { 37 | printf("vx() returns: %d\n", t->vx()); 38 | } 39 | 40 | ]]; 41 | 42 | enum "testEnum" { 43 | "enum1", 44 | "enum2", 45 | "enum3", 46 | "enum4", 47 | "enum5", 48 | }; 49 | 50 | class "test" { 51 | func "new" :args(int "x":opt(0)); 52 | virtual "vx" :rets "int"; 53 | func "x" :rets "int"; 54 | func "proc" :args(callback "f"(int):rets(int)) :rets "int"; 55 | }; 56 | 57 | func "virt_test" :args(class_ptr "test" "t") :rets(int); 58 | }; 59 | 60 | -- first way to change: use a table contain new things. 61 | test.test { 62 | func "new" :args(int "x":opt(2)); -- select and change default argument. 63 | }; 64 | 65 | -- second way to change: use add/hide to select items. 66 | test.testEnum 67 | :add 'enum6' (1000) -- add a new enum item. 68 | 69 | -- add a alias of type. 70 | test.testEnum 71 | :alias "myEnum" 72 | 73 | gen(exports) 74 | --------------------------------------------------------------------------------