Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Yes, hi, this file is plaintext and you are reading it! If you can find a way to break it, please let us know.
- -- These files are all wrapped in a setfenv so we don't accidentally pollute the global table (and, conversely, so the global table can't be changed to muck with us.)
- -- We're not importing _G in implicitly via metatable because we don't want to rely on it for *anything* - every import needs to be cached locally, before user addons can get in and start replacing functions.
- -- In reality, even if this all existed in userspace, we'd still be "safe" - the addon environment just doesn't have the hooks exposed to do anything sensitive - but I have no interest in debugging problems caused by somebody reimplementing ipairs or select.
- local addonLookupTable
- local errorEventSpawner
- local performanceStackPush
- local performanceStackPop
- function setEventFunctions(alt, ees, pspush, pspop)
- addonLookupTable = alt
- errorEventSpawner = ees
- performanceStackPush = pspush
- performanceStackPop = pspop
- end
- local ipairs = _G.ipairs
- local xpcall = _G.xpcall
- local print = _G.print
- local assert = _G.assert
- local error = _G.error
- local type = _G.type
- local select = _G.select
- local unpack = _G.unpack
- local gmatch = _G.string.gmatch
- local rawget = _G.rawget
- local gsub = _G.string.gsub
- local format = _G.string.format
- local tinsert = _G.table.insert
- local loadstring = _G.loadstring
- local traceback = _G.debug.traceback
- local tostring = _G.tostring
- local collectgarbage = _G.collectgarbage
- -- Set this up now, we'll fill it via eventRegister
- _G.Event = {}
- local eventNameLookup = {}
- function traverseAndCreate(eventname)
- local eventtable = _G.Event
- -- Traverse down the tree based on our event name
- for word in gmatch(eventname, "([^_]+)") do
- if not eventtable[word] then
- eventtable[word] = {}
- end
- eventtable = eventtable[word]
- end
- return eventtable
- end
- function eventRegister(eventname)
- local eventtable = traverseAndCreate(eventname)
- -- Add an entry to our lookup table
- eventNameLookup[eventtable] = gsub(eventname, "_", ".")
- documentedObjects[gsub(eventname, "_", ".")] = {eventtable, "(Event documentation TBI)"}
- documentedObjectLookup[eventtable] = gsub(eventname, "_", ".")
- return eventtable
- end
- function slashRegister(slashname)
- -- We can't use the traverseAndCreate function because we might have actual underscores, so we'll do something a little hacky
- local eventtable = traverseAndCreate("Slash")
- assert(not eventtable[slashname])
- eventtable[slashname] = {}
- eventtable = eventtable[slashname]
- eventNameLookup[eventtable] = "Slash." .. slashname
- documentedObjects["Event.Slash." .. slashname] = {eventtable, "(Slash event documentation TBI)"}
- documentedObjectLookup[eventtable] = "Event.Slash." .. slashname
- return eventtable
- end
- -- We make this so we can have the stub in place
- traverseAndCreate("Slash")
- -- Since errors spawn events, but events can spawn errors, we put a little test here. If we end up in a recursive error, we drop out.
- -- We do permit one extra stack depth to make it easier to debug error handlers, however ;)
- local errorHandlerDepth = 0
- function errorHandler(event_name, traceback, hook, err)
- if errorHandlerDepth > 1 then return end
- errorHandlerDepth = errorHandlerDepth + 1
- errorEventSpawner(event_name, rawget(hook, 2), rawget(hook, 3), err, traceback)
- errorHandlerDepth = errorHandlerDepth - 1
- end
- function eventHookDispatcher(eventtable, ...)
- local parameters = {...}
- local parametercount = select("#", ...)
- -- We wrap this in an xpcall in case someone's done something nasty to the hooktable metatable
- local success, result = xpcall(function ()
- for _, v in ipairs(eventtable) do
- local pushed = false
- -- And we wrap the individual calls in xpcalls so that an error in one addon won't trash other addons
- local success, result = xpcall(function ()
- -- We want to be able to identify our own hooks as ours, but still not let other people masquerade as us
- -- Right now we're just ignoring the second half of that. We can always retrofit that into the system in a patch.
- local privileged = (rawget(v, 2) == "Rift")
- -- The table we've been given may have some metatable craziness so we use raw gets for everything, as we want to be able to report data accurately
- -- Verify that the hook is set up correctly (if it's not, we'll just mash the error handler)
- if type(rawget(v, 2)) ~= "string" then
- error(format("Event hook in %s has a addon identifier of type %s, not string", eventNameLookup[eventtable], type(rawget(v, 2))))
- end
- if not addonLookupTable[rawget(v, 2)] and not privileged then
- error(format("Event hook in %s refers to an unknown addon \"%s\"", eventNameLookup[eventtable], rawget(v, 2)))
- end
- if type(rawget(v, 3)) ~= "string" then
- error(format("Event hook in %s has a hook identifier of type %s, not string", eventNameLookup[eventtable], type(rawget(v, 3))))
- end
- -- Push a stack in the performance monitor
- performanceStackPush(rawget(v, 2), rawget(v, 3))
- pushed = true
- -- Returning values is currently an error, so we don't permit it.
- assert(select("#", rawget(v, 1)(unpack(parameters, 1, parametercount))) == 0)
- end, function (err)
- return {traceback = traceback(), err = err}
- end)
- if pushed then
- -- Pop that stack
- performanceStackPop()
- end
- if not success then
- errorHandler(eventNameLookup[eventtable], result.traceback, v, result.err)
- end
- end
- end, function (err)
- return {traceback = traceback(), err = err}
- end)
- if not success then
- errorHandler(eventNameLookup[eventtable], result.traceback, {nil, "Rift", eventNameLookup[eventtable] .. " table iteration"}, result.err)
- end
- end
- function frameEventHookDispatcher(frameTable, eventTable, handle, owner, ident)
- local func = rawget(eventTable, handle)
- if func == nil then
- return false
- end
- local success, result = xpcall(function ()
- assert(select("#", func(frameTable)) == 0)
- end, function (err)
- return {traceback = traceback(), err = err}
- end)
- if not success then
- errorHandler(ident, result.traceback, {nil, owner, handle}, result.err)
- end
- return true -- even if we have an error, because if the error is fixed, we won't get a notification
- end
- function runString(str)
- local success, result = xpcall(function ()
- assert(loadstring(str))()
- end, function (err)
- return {traceback = traceback(), err = err}
- end)
- if not success then
- errorHandler("Console", result.traceback, {nil, "Rift", str}, result.err)
- end
- end
- -- Runs a pre-supplied function that's been generated from a file
- function runFile(addonIdentifier, func)
- local success, result = xpcall(function ()
- assert(select("#", func()) == 0)
- end, function (err)
- return {traceback = traceback(), err = err}
- end)
- if not success then
- errorHandler("Load", result.traceback, {nil, addonIdentifier, "Run"}, result.err)
- end
- end
- local function safetostring(param)
- if type(param) == "string" or type(param) == "number" then
- return tostring(param)
- else
- return type(param)
- end
- end
- function insertDefaultEvents()
- -- System error handler
- tinsert(_G.Event.System.Error, {function (err, event, addon, hook, traceback)
- print("---------")
- print("Error: " .. safetostring(err))
- print(format(" In %s / %s, event %s", safetostring(addon), safetostring(hook), safetostring(event)))
- print(safetostring(traceback))
- end, "Rift", "Default error hook"})
- -- System garbage collector
- tinsert(_G.Event.System.Update.End, {function ()
- collectgarbage("stop")
- collectgarbage("step", 1)
- end, "Rift", "Garbage Collector"})
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement