mcserver/source/virtual_method_hooks.lua

507 lines
12 KiB
Lua
Raw Normal View History

-- flags
local disable_virtual_hooks = false
local enable_pure_virtual = true
local default_private_access = false
local access = {public = 0, protected = 1, private = 2}
function preparse_hook(p)
if default_private_access then
-- we need to make all structs 'public' by default
p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
end
end
function parser_hook(s)
local container = classContainer.curr -- get the current container
if default_private_access then
if not container.curr_member_access and container.classtype == 'class' then
-- default access for classes is private
container.curr_member_access = access.private
end
end
-- try labels (public, private, etc)
do
local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
if b then
-- found a label, get the new access value from the global 'access' table
if access[label] then
container.curr_member_access = access[label]
end -- else ?
return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
end
end
local ret = nil
if disable_virtual_hooks then
return ret
end
local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
local const
if b then
local ret = string.sub(s, e+1)
if string.find(ret, "^%s*const") then
const = "const"
ret = string.gsub(ret, "^%s*const", "")
end
local purev = false
if string.find(ret, "^%s*=%s*0") then
purev = true
ret = string.gsub(ret, "^%s*=%s*0", "")
end
ret = string.gsub(ret, "^%s*%b{}", "")
local func = Function(decl, arg, const)
func.pure_virtual = purev
--func.access = access
func.original_sig = decl
local curflags = classContainer.curr.flags
if not curflags.virtual_class then
curflags.virtual_class = VirtualClass()
end
curflags.virtual_class:add(func)
curflags.pure_virtual = curflags.pure_virtual or purev
return ret
end
return ret
end
-- class VirtualClass
classVirtualClass = {
classtype = 'class',
name = '',
base = '',
type = '',
btype = '',
ctype = '',
}
classVirtualClass.__index = classVirtualClass
setmetatable(classVirtualClass,classClass)
function classVirtualClass:add(f)
local parent = classContainer.curr
pop()
table.insert(self.methods, {f=f})
local name,sig
-- doble negative means positive
if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
name = self.original_name
elseif f.name == 'delete' then
name = '~'..self.original_name
else
if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
end
end
if name then
sig = name..self:get_arg_list(f, true)..";\n"
push(self)
sig = preprocess(sig)
self:parse(sig)
pop()
end
push(parent)
end
function preprocess(sig)
sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
return sig
end
function classVirtualClass:get_arg_list(f, decl)
local ret = ""
local sep = ""
local i=1
while f.args[i] do
local arg = f.args[i]
if decl then
local ptr
if arg.ret ~= '' then
ptr = arg.ret
else
ptr = arg.ptr
end
local def = ""
if arg.def and arg.def ~= "" then
def = " = "..arg.def
end
ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
else
ret = ret..sep..arg.name
end
sep = ","
i = i+1
end
return "("..ret..")"
end
function classVirtualClass:add_parent_virtual_methods(parent)
parent = parent or _global_classes[self.flags.parent_object.btype]
if not parent then return end
if parent.flags.virtual_class then
local vclass = parent.flags.virtual_class
for k,v in ipairs(vclass.methods) do
if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
table.insert(self.methods, {f=v.f})
end
end
end
parent = _global_classes[parent.btype]
if parent then
self:add_parent_virtual_methods(parent)
end
end
function classVirtualClass:has_method(f)
for k,v in pairs(self.methods) do
-- just match name for now
if v.f.name == f.name then
return true
end
end
return false
end
function classVirtualClass:add_constructors()
local i=1
while self.flags.parent_object[i] do
local v = self.flags.parent_object[i]
if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
self:add(v)
end
i = i+1
end
end
--[[
function classVirtualClass:requirecollection(t)
self:add_constructors()
local req = classClass.requirecollection(self, t)
if req then
output('class ',self.name,";")
end
return req
end
--]]
function classVirtualClass:supcode()
-- pure virtual classes can have no default constructors on gcc 4
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
end
local ns
if self.prox.classtype == 'namespace' then
output('namespace ',self.prox.name, " {")
ns = true
end
output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
output("public:\n")
self:add_parent_virtual_methods()
self:output_methods(self.btype)
self:output_parent_methods()
self:add_constructors()
-- no constructor for pure virtual classes
if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
self:output_constructors()
end
output("};\n\n")
if ns then
output("};")
end
classClass.supcode(self)
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#endif // __GNUC__ >= 4\n')
end
-- output collector for custom class if required
if self:requirecollection(_collect) and _collect[self.type] then
output('\n')
output('/* function to release collected object via destructor */')
output('#ifdef __cplusplus\n')
--for i,v in pairs(collect) do
i,v = self.type, _collect[self.type]
output('\nstatic int '..v..' (lua_State* tolua_S)')
output('{')
output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
output(' delete self;')
output(' return 0;')
output('}')
--end
output('#endif\n\n')
end
end
function classVirtualClass:register(pre)
-- pure virtual classes can have no default constructors on gcc 4
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
end
classClass.register(self, pre)
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
output('#endif // __GNUC__ >= 4\n')
end
end
--function classVirtualClass:requirecollection(_c)
-- if self.flags.parent_object.flags.pure_virtual then
-- return false
-- end
-- return classClass.requirecollection(self, _c)
--end
function classVirtualClass:output_parent_methods()
for k,v in ipairs(self.methods) do
if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
local parent_name = rettype..self.btype.."__"..v.f.name
local par_list = self:get_arg_list(v.f, true)
local var_list = self:get_arg_list(v.f, false)
-- the parent's virtual function
output("\t"..parent_name..par_list.." {")
output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
output("\t};")
end
end
end
function classVirtualClass:output_methods(btype)
for k,v in ipairs(self.methods) do
if v.f.name ~= 'new' and v.f.name ~= 'delete' then
self:output_method(v.f, btype)
end
end
output("\n")
end
function classVirtualClass:output_constructors()
for k,v in ipairs(self.methods) do
if v.f.name == 'new' then
local par_list = self:get_arg_list(v.f, true)
local var_list = self:get_arg_list(v.f, false)
output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
end
end
end
function classVirtualClass:output_method(f, btype)
if f.access == 2 then -- private
return
end
local ptr
if f.ret ~= '' then
ptr = f.ret
else
ptr = f.ptr
end
local rettype = f.mod.." "..f.type..f.ptr.." "
local par_list = self:get_arg_list(f, true)
local var_list = self:get_arg_list(f, false)
if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
_,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
end
-- the caller of the lua method
output("\t"..rettype.." "..f.name..par_list..f.const.." {")
local fn = f.cname
if f.access == 1 then
fn = "NULL"
end
output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
--if f.type ~= 'void' then
-- output("\t\t\tint top = lua_gettop(lua_state)-1;")
--end
-- push the parameters
local argn = 0
for i,arg in ipairs(f.args) do
if arg.type ~= 'void' then
local t,ct = isbasic(arg.type)
if t and t ~= '' then
if arg.ret == "*" then
t = 'userdata'
ct = 'void*'
end
output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
else
local m = arg.ptr
if m and m~= "" then
if m == "*" then m = "" end
output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
else
output("\t\t\tvoid* tolua_obj" .. argn .." = (void*)new "..arg.type.."("..arg.name..");\n")
output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj' .. argn .. ', "'..arg.type..'");\n')
end
end
argn = argn+1
end
end
-- call the function
output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
-- return value
if f.type ~= 'void' then
output("1);")
local t,ct = isbasic(f.type)
if t and t ~= '' then
--output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
else
local mod = ""
if f.ptr ~= "*" then
mod = "*("..f.type.."*)"
end
--output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
end
output("\t\t\tlua_pop(lua_state, 1);")
output("\t\t\treturn tolua_ret;")
else
output("0);")
end
-- handle non-implemeted function
output("\t\t} else {")
if f.pure_virtual then
output('\t\t\tif (lua_state)')
--output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
output('\t\t\telse {')
output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
output('\t\t\t\t::abort();')
output('\t\t\t};')
if( rettype == " std::string " ) then
output('\t\t\treturn "";')
else
output('\t\t\treturn (',rettype,')0;')
end
else
output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
end
output("\t\t};")
output("\t};")
end
function VirtualClass()
local parent = classContainer.curr
pop()
local name = "Lua__"..parent.original_name
local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
setmetatable(c, classVirtualClass)
local ft = getnamespace(c.parent)..c.original_name
append_global_type(ft, c)
push(parent)
c.flags.parent_object = parent
c.methods = {}
push(c)
c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
pop()
return c
end