local MAJOR_VERSION = "LibDogTag-3.0" local MINOR_VERSION = tonumber(("@project-date-integer@"):match("%d+")) or 33333333333333 if MINOR_VERSION > _G.DogTag_MINOR_VERSION then _G.DogTag_MINOR_VERSION = MINOR_VERSION end local _G, pairs, ipairs, type, table, tostring, rawget, assert, next, select, error, setmetatable, unpack, next = _G, pairs, ipairs, type, table, tostring, rawget, assert, next, select, error, setmetatable, unpack, next -- #AUTODOC_NAMESPACE DogTag DogTag_funcs[#DogTag_funcs+1] = function(DogTag) local poolNum = 0 local newList, newDict, newSet, del, deepDel, deepCopy do local pool = setmetatable({}, {__mode='k'}) function newList(...) poolNum = poolNum + 1 local t = next(pool) if t then pool[t] = nil for i = 1, select('#', ...) do t[i] = select(i, ...) end else t = { ... } end -- if TABLE_DEBUG and pool == normalPool then -- TABLE_DEBUG[#TABLE_DEBUG+1] = { '***', "newList", poolNum, tostring(t), debugstack() } -- end return t end function newDict(...) poolNum = poolNum + 1 local t = next(pool) if t then pool[t] = nil else t = {} end for i = 1, select('#', ...), 2 do t[select(i, ...)] = select(i+1, ...) end -- if TABLE_DEBUG and pool == normalPool then -- TABLE_DEBUG[#TABLE_DEBUG+1] = { '***', "newDict", poolNum, tostring(t), debugstack() } -- end return t end function newSet(...) poolNum = poolNum + 1 local t = next(pool) if t then pool[t] = nil else t = {} end for i = 1, select('#', ...) do t[select(i, ...)] = true end -- if TABLE_DEBUG and pool == normalPool then -- TABLE_DEBUG[#TABLE_DEBUG+1] = { '***', "newSet", poolNum, tostring(t), debugstack() } -- end return t end function del(t) if type(t) ~= "table" then error(("Bad argument #1 to `del'. Expected %q, got %q (%s)."):format("table", type(t), tostring(t)), 2) end if pool[t] then error("Double-free syndrome.", 2) end pool[t] = true poolNum = poolNum - 1 for k in pairs(t) do t[k] = nil end setmetatable(t, nil) t[''] = true t[''] = nil -- if TABLE_DEBUG then -- local tostring_t = tostring(t) -- TABLE_DEBUG[#TABLE_DEBUG+1] = { '***', "del", poolNum, tostring_t, debugstack() } -- for _, line in ipairs(TABLE_DEBUG) do -- if line[4] == tostring_t then -- line[1] = '' -- end -- end -- pool[t] = nil -- end return nil end local deepDel_data function deepDel(t) local made_deepDel_data = not deepDel_data if made_deepDel_data then deepDel_data = newList() end if type(t) == "table" and not deepDel_data[t] then deepDel_data[t] = true for k,v in pairs(t) do deepDel(v) deepDel(k) end del(t) end if made_deepDel_data then deepDel_data = del(deepDel_data) end return nil end function deepCopy(t) if type(t) ~= "table" then return t else local u = newList() for k, v in pairs(t) do u[deepCopy(k)] = deepCopy(v) end return u end end end DogTag.newList, DogTag.newDict, DogTag.newSet, DogTag.del, DogTag.deepDel, DogTag.deepCopy = newList, newDict, newSet, del, deepDel, deepCopy local DEBUG = _G.DogTag_DEBUG -- set in test.lua if DEBUG then DogTag.getPoolNum = function() return poolNum end DogTag.setPoolNum = function(value) poolNum = value end end local function sortStringList(s) if not s then return nil end local set = newSet((";"):split(s)) local list = newList() for k in pairs(set) do list[#list+1] = k end set = del(set) table.sort(list) local q = table.concat(list, ';') list = del(list) return q end DogTag.sortStringList = sortStringList local function select2(min, max, ...) if min <= max then return select(min, ...), select2(min+1, max, ...) end end DogTag.select2 = select2 local function joinSet(set, connector) local t = newList() for k in pairs(set) do t[#t+1] = k end table.sort(t) local s = table.concat(t, connector) t = del(t) return s end DogTag.joinSet = joinSet local fixNamespaceList = setmetatable({}, {__index = function(self, nsList) if not nsList then return self[""] end local t = newSet((";"):split(nsList)) t[""] = nil t["Base"] = true local s = joinSet(t, ';') t = del(t) self[nsList] = s return s end}) DogTag.fixNamespaceList = fixNamespaceList local unpackNamespaceList = setmetatable({}, {__index = function(self, key) local t = {(";"):split(key)} self[key] = t return t end, __call = function(self, key) return unpack(self[key]) end}) DogTag.unpackNamespaceList = unpackNamespaceList local function getASTType(ast) if not ast then return "nil" end local type_ast = type(ast) if type_ast ~= "table" then return type_ast end return ast[1] end DogTag.getASTType = getASTType local memoizeTable do local function key_sort(alpha, bravo) local type_alpha, type_bravo = type(alpha), type(bravo) if type_alpha ~= type_bravo then return type_alpha < type_bravo end if type_alpha == "string" or type_alpha == "number" then return alpha < bravo elseif type_alpha == "boolean" then return not alpha and bravo elseif type_alpha == "table" then local alpha_len, bravo_len = #alpha, #bravo if alpha_len ~= bravo_len then return alpha_len < bravo_len end for i, v in ipairs(alpha) do local u = bravo[i] local one = key_sort(v, u) if not one then local two = key_sort(u, v) if two then return false end else return true end end local alpha_klen, bravo_klen = 0, 0 for k in pairs(alpha) do alpha_klen = alpha_klen + 1 end for k in pairs(bravo) do bravo_klen = bravo_klen + 1 end if alpha_klen ~= bravo_klen then return alpha_klen < bravo_klen end if alpha_klen ~= alpha_len then local alpha_keys, bravo_keys = newList(), newList() for k in pairs(alpha) do alpha_keys[#alpha_keys+1] = k end table.sort(alpha_keys, key_sort) for k in pairs(bravo) do bravo_keys[#bravo_keys+1] = k end table.sort(bravo_keys, key_sort) for i, k in ipairs(alpha_keys) do local l = bravo_keys[i] local one = key_sort(k, l) if not one then local two = key_sort(l, k) if two then alpha_keys, bravo_keys = del(alpha_keys), del(bravo_keys) return false end else alpha_keys, bravo_keys = del(alpha_keys), del(bravo_keys) return true end local v, u = alpha[k], bravo[l] local one = key_sort(v, u) if not one then local two = key_sort(u, v) if two then alpha_keys, bravo_keys = del(alpha_keys), del(bravo_keys) return false end else alpha_keys, bravo_keys = del(alpha_keys), del(bravo_keys) return true end end alpha_keys, bravo_keys = del(alpha_keys), del(bravo_keys) end return false end return false end local function tableToString(tab, t) local type_tab = type(tab) if type_tab ~= "table" then if type_tab == "number" or type_tab == "string" then t[#t+1] = tab else t[#t+1] = tostring(tab) end return end local keys = newList() for k in pairs(tab) do keys[#keys+1] = k end table.sort(keys, key_sort) for _, k in ipairs(keys) do local v = tab[k] tableToString(k, t) t[#t+1] = '=' tableToString(v, t) t[#t+1] = ';' end keys = del(keys) end local pool = setmetatable({}, {__mode='v'}) function memoizeTable(tab) if type(tab) ~= "table" then return tab end local t = newList() tableToString(tab, t) local key = table.concat(t) t = del(t) local pool_key = pool[key] if pool_key then deepDel(tab) return pool_key else pool[key] = tab return tab end end end DogTag.memoizeTable = memoizeTable local function deepCompare(t1,t2) local ty1 = type(t1) local ty2 = type(t2) if ty1 ~= ty2 then return false end -- non-table types can be directly compared if ty1 ~= 'table' and ty2 ~= 'table' then return t1 == t2 end for k1,v1 in pairs(t1) do local v2 = t2[k1] if v2 == nil or not deepCompare(v1,v2) then return false end end for k2,v2 in pairs(t2) do local v1 = t1[k2] if v1 == nil or not deepCompare(v1,v2) then return false end end return true end DogTag.deepCompare = deepCompare local kwargsToKwargTypes, kwargsToKwargTypesWithTableCache do kwargsToKwargTypes = setmetatable({}, { __index = function(self, kwargs) if not kwargs then return self[""] elseif kwargs == "" then local t = {} self[false] = t self[""] = t return t end local kwargTypes = newList() local keys = newList() for k in pairs(kwargs) do keys[#keys+1] = k end table.sort(keys) local t = newList() for i,k in ipairs(keys) do if i > 1 then t[#t+1] = ";" end local v = kwargs[k] t[#t+1] = k t[#t+1] = "=" local type_v = type(v) t[#t+1] = type_v kwargTypes[k] = type_v end keys = del(keys) local s = table.concat(t) t = del(t) local self_s = rawget(self, s) if self_s then kwargTypes = del(kwargTypes) return self_s end local t = {} for k, v in pairs(kwargTypes) do t[k] = v end kwargTypes = del(kwargTypes) kwargTypes = t self[s] = kwargTypes return kwargTypes end, __mode='kv' }) -- This is separate from kwargsToKwargTypes because in some cases, -- a reused kwargs (reused by the addon providing it to DogTag) -- will cause a possibly incorrect kwargTypes table to be returned. -- This cache should only be used in places where we are certain that the kwargs -- table is not being reused - this is usually true if: -- We memoizeTable(deepCopy(kwargs)) -- Or if kwargs is obtained from fsToKwargs[fs] or callbackToKwargs[uid] -- (because all kwargs in these tables have had memoizeTable(deepCopy(kwargs)) performed) kwargsToKwargTypesWithTableCache = setmetatable({}, { __index = function(self, kwargs) local kwargTypes = kwargsToKwargTypes[kwargs] if not kwargs then return kwargTypes end self[kwargs] = kwargTypes return kwargTypes end, __mode='kv' }) end DogTag.kwargsToKwargTypes = kwargsToKwargTypes DogTag.kwargsToKwargTypesWithTableCache = kwargsToKwargTypesWithTableCache local codeToFunction, codeToEventList, callbackToNSList, callbackToCode, callbackToKwargTypes, eventData local fsToNSList, fsToKwargs, fsToCode, fsNeedQuickUpdate DogTag_funcs[#DogTag_funcs+1] = function() codeToFunction = DogTag.codeToFunction codeToEventList = DogTag.codeToEventList callbackToNSList = DogTag.callbackToNSList callbackToCode = DogTag.callbackToCode callbackToKwargTypes = DogTag.callbackToKwargTypes eventData = DogTag.eventData fsToNSList = DogTag.fsToNSList fsToKwargs = DogTag.fsToKwargs fsToCode = DogTag.fsToCode fsNeedQuickUpdate = DogTag.fsNeedQuickUpdate end local codesToClear = {} local function _clearCodes() if not next(codesToClear) then return end for nsList, codeToFunction_nsList in pairs(codeToFunction) do for _, ns in ipairs(unpackNamespaceList[nsList]) do if codesToClear[ns] then for kwargTypes, d in pairs(codeToFunction_nsList) do if type(kwargTypes) ~= "number" then codeToFunction_nsList[kwargTypes] = del(d) end end codeToFunction[nsList] = del(codeToFunction_nsList) break end end end for nsList, codeToEventList_nsList in pairs(codeToEventList) do for _, ns in ipairs(unpackNamespaceList[nsList]) do if codesToClear[ns] then for kwargTypes, d in pairs(codeToEventList_nsList) do if type(kwargTypes) ~= "number" then codeToEventList_nsList[kwargTypes] = del(d) end end codeToEventList[nsList] = del(codeToEventList_nsList) break end end end for event, eventData_event in pairs(eventData) do eventData[event] = del(eventData_event) end for uid, nsList in pairs(callbackToNSList) do for _, ns in ipairs(unpackNamespaceList[nsList]) do if codesToClear[ns] then local kwargTypes = callbackToKwargTypes[uid] local code = callbackToCode[uid] local eventList = codeToEventList[nsList][kwargTypes][code] if eventList == nil then local _ = codeToFunction[nsList][kwargTypes][code] eventList = codeToEventList[nsList][kwargTypes][code] assert(eventList ~= nil) end break end end end for fs, nsList in pairs(fsToNSList) do local kwargs = fsToKwargs[fs] local code = fsToCode[fs] local kwargTypes = kwargsToKwargTypesWithTableCache[kwargs] local codeToEventList_nsList_kwargTypes_code = codeToEventList[nsList][kwargTypes][code] if codeToEventList_nsList_kwargTypes_code == nil then local _ = codeToFunction[nsList][kwargTypes][code] codeToEventList_nsList_kwargTypes_code = codeToEventList[nsList][kwargTypes][code] assert(codeToEventList_nsList_kwargTypes_code ~= nil) end if codeToEventList_nsList_kwargTypes_code then for event, arg in pairs(codeToEventList_nsList_kwargTypes_code) do eventData[event][fs] = arg DogTag.eventUsed(event) end end fsNeedQuickUpdate[fs] = true end for k in pairs(codesToClear) do codesToClear[k] = nil end end DogTag._clearCodes = _clearCodes local function clearCodes(namespace) codesToClear[namespace or ''] = true end DogTag.clearCodes = clearCodes function DogTag.tagError(code, nsList, err) local _, minor = LibStub(MAJOR_VERSION) local message = ("%s.%d: Error with code %q (%s). %s"):format(MAJOR_VERSION, minor/1000000, code, nsList, err) geterrorhandler()(message) return message, code, nsList, err end end