vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 Name: AceLibrary
3 Revision: $Rev: 13374 $
4 Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team)
5 Inspired By: Iriel (iriel@vigilance-committee.org)
6 Tekkub (tekkub@gmail.com)
7 Revision: $Rev: 13374 $
8 Website: http://www.wowace.com/
9 Documentation: http://www.wowace.com/index.php/AceLibrary
10 SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary
11 Description: Versioning library to handle other library instances, upgrading,
12 and proper access.
13 It also provides a base for libraries to work off of, providing
14 proper error tools. It is handy because all the errors occur in the
15 file that called it, not in the library file itself.
16 Dependencies: None
17 ]]
18  
19 local ACELIBRARY_MAJOR = "AceLibrary"
20 local ACELIBRARY_MINOR = "$Revision: 13374 $"
21  
22 local table_setn
23 do
24 local version = GetBuildInfo()
25 if string.find(version, "^2%.") then
26 -- 2.0.0
27 table_setn = function() end
28 else
29 table_setn = table.setn
30 end
31 end
32  
33 local string_gfind = string.gmatch or string.gfind
34  
35 local _G = getfenv(0)
36 local previous = _G[ACELIBRARY_MAJOR]
37 if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end
38  
39 -- @table AceLibrary
40 -- @brief System to handle all versioning of libraries.
41 local AceLibrary = {}
42 local AceLibrary_mt = {}
43 setmetatable(AceLibrary, AceLibrary_mt)
44  
45 local tmp
46 local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
47 if type(self) ~= "table" then
48 _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2)
49 end
50 if not tmp then
51 tmp = {}
52 else
53 for k in pairs(tmp) do tmp[k] = nil end
54 table_setn(tmp, 0)
55 end
56  
57 table.insert(tmp, a1)
58 table.insert(tmp, a2)
59 table.insert(tmp, a3)
60 table.insert(tmp, a4)
61 table.insert(tmp, a5)
62 table.insert(tmp, a6)
63 table.insert(tmp, a7)
64 table.insert(tmp, a8)
65 table.insert(tmp, a9)
66 table.insert(tmp, a10)
67 table.insert(tmp, a11)
68 table.insert(tmp, a12)
69 table.insert(tmp, a13)
70 table.insert(tmp, a14)
71 table.insert(tmp, a15)
72 table.insert(tmp, a16)
73 table.insert(tmp, a17)
74 table.insert(tmp, a18)
75 table.insert(tmp, a19)
76 table.insert(tmp, a20)
77  
78 local stack = debugstack()
79 if not message then
80 local _,_,second = string.find(stack, "\n(.-)\n")
81 message = "error raised! " .. second
82 else
83 for i = 1,table.getn(tmp) do
84 tmp[i] = tostring(tmp[i])
85 end
86 for i = 1,10 do
87 table.insert(tmp, "nil")
88 end
89 message = string.format(message, unpack(tmp))
90 end
91  
92 if getmetatable(self) and getmetatable(self).__tostring then
93 message = string.format("%s: %s", tostring(self), message)
94 elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then
95 message = string.format("%s: %s", self:GetLibraryVersion(), message)
96 elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then
97 message = string.format("%s: %s", self.class:GetLibraryVersion(), message)
98 end
99  
100 local first = string.gsub(stack, "\n.*", "")
101 local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1")
102 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
103  
104 local i = 0
105 for s in string_gfind(stack, "\n([^\n]*)") do
106 i = i + 1
107 if not string.find(s, file .. "%.lua:%d+:") then
108 file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1")
109 file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1")
110 break
111 end
112 end
113 local j = 0
114 for s in string_gfind(stack, "\n([^\n]*)") do
115 j = j + 1
116 if j > i and not string.find(s, file .. "%.lua:%d+:") then
117 _G.error(message, j + 1)
118 return
119 end
120 end
121 _G.error(message, 2)
122 return
123 end
124  
125 local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
126 if not condition then
127 if not message then
128 local stack = debugstack()
129 local _,_,second = string.find(stack, "\n(.-)\n")
130 message = "assertion failed! " .. second
131 end
132 error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
133 return
134 end
135 return condition
136 end
137  
138 local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5)
139 if type(num) ~= "number" then
140 error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num))
141 elseif type(kind) ~= "string" then
142 error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind))
143 end
144 local errored = false
145 arg = type(arg)
146 if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then
147 local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])")
148 if not func then
149 _,_,func = string.find(debugstack(), "([`<].-['>])")
150 end
151 if kind5 then
152 error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg)
153 elseif kind4 then
154 error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg)
155 elseif kind3 then
156 error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg)
157 elseif kind2 then
158 error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg)
159 else
160 error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg)
161 end
162 end
163 end
164  
165 local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
166 a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
167 if not a1 then
168 error(self, string.gsub(a2, ".-%.lua:%d-: ", ""))
169 else
170 return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20
171 end
172 end
173  
174 local recurse = {}
175 local function addToPositions(t, major)
176 if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then
177 rawset(t, recurse, true)
178 AceLibrary.positions[t] = major
179 for k,v in pairs(t) do
180 if type(v) == "table" and not rawget(v, recurse) then
181 addToPositions(v, major)
182 end
183 if type(k) == "table" and not rawget(k, recurse) then
184 addToPositions(k, major)
185 end
186 end
187 local mt = getmetatable(t)
188 if mt and not rawget(mt, recurse) then
189 addToPositions(mt, major)
190 end
191 rawset(t, recurse, nil)
192 end
193 end
194  
195 local function svnRevisionToNumber(text)
196 if type(text) == "string" then
197 if string.find(text, "^%$Revision: (%d+) %$$") then
198 return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1")))
199 elseif string.find(text, "^%$Rev: (%d+) %$$") then
200 return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1")))
201 elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then
202 return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1")))
203 end
204 elseif type(text) == "number" then
205 return text
206 end
207 return nil
208 end
209  
210 local crawlReplace
211 do
212 local recurse = {}
213 local function func(t, to, from)
214 if recurse[t] then
215 return
216 end
217 recurse[t] = true
218 local mt = getmetatable(t)
219 setmetatable(t, nil)
220 rawset(t, to, rawget(t, from))
221 rawset(t, from, nil)
222 for k,v in pairs(t) do
223 if v == from then
224 t[k] = to
225 elseif type(v) == "table" then
226 if not recurse[v] then
227 func(v, to, from)
228 end
229 end
230  
231 if type(k) == "table" then
232 if not recurse[k] then
233 func(k, to, from)
234 end
235 end
236 end
237 setmetatable(t, mt)
238 if mt then
239 if mt == from then
240 setmetatable(t, to)
241 elseif not recurse[mt] then
242 func(mt, to, from)
243 end
244 end
245 end
246 function crawlReplace(t, to, from)
247 func(t, to, from)
248 for k in pairs(recurse) do
249 recurse[k] = nil
250 end
251 end
252 end
253  
254 -- @function destroyTable
255 -- @brief remove all the contents of a table
256 -- @param t table to destroy
257 local function destroyTable(t)
258 setmetatable(t, nil)
259 for k,v in pairs(t) do t[k] = nil end
260 table_setn(t, 0)
261 end
262  
263 local function isFrame(frame)
264 return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function"
265 end
266  
267 local new, del
268 do
269 local tables = setmetatable({}, {__mode = "k"})
270  
271 function new()
272 local t = next(tables)
273 if t then
274 tables[t] = nil
275 return t
276 else
277 return {}
278 end
279 end
280  
281 function del(t, depth)
282 if depth and depth > 0 then
283 for k,v in pairs(t) do
284 if type(v) == "table" and not isFrame(v) then
285 del(v, depth - 1)
286 end
287 end
288 end
289 destroyTable(t)
290 tables[t] = true
291 end
292 end
293  
294 -- @function copyTable
295 -- @brief Create a shallow copy of a table and return it.
296 -- @param from The table to copy from
297 -- @return A shallow copy of the table
298 local function copyTable(from)
299 local to = new()
300 for k,v in pairs(from) do to[k] = v end
301 table_setn(to, table.getn(from))
302 setmetatable(to, getmetatable(from))
303 return to
304 end
305  
306 -- @function deepTransfer
307 -- @brief Fully transfer all data, keeping proper previous table
308 -- backreferences stable.
309 -- @param to The table with which data is to be injected into
310 -- @param from The table whose data will be injected into the first
311 -- @param saveFields If available, a shallow copy of the basic data is saved
312 -- in here.
313 -- @param list The account of table references
314 -- @param list2 The current status on which tables have been traversed.
315 local deepTransfer
316 do
317 -- @function examine
318 -- @brief Take account of all the table references to be shared
319 -- between the to and from tables.
320 -- @param to The table with which data is to be injected into
321 -- @param from The table whose data will be injected into the first
322 -- @param list An account of the table references
323 local function examine(to, from, list, major)
324 list[from] = to
325 for k,v in pairs(from) do
326 if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then
327 if from[k] == to[k] then
328 list[from[k]] = to[k]
329 elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then
330 list[from[k]] = from[k]
331 elseif not list[from[k]] then
332 examine(to[k], from[k], list, major)
333 end
334 end
335 end
336 return list
337 end
338  
339 function deepTransfer(to, from, saveFields, major, list, list2)
340 setmetatable(to, nil)
341 local createdList
342 if not list then
343 createdList = true
344 list = new()
345 list2 = new()
346 examine(to, from, list, major)
347 end
348 list2[to] = to
349 for k,v in pairs(to) do
350 if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then
351 if saveFields then
352 saveFields[k] = v
353 end
354 to[k] = nil
355 elseif v ~= _G then
356 if saveFields then
357 saveFields[k] = copyTable(v)
358 end
359 end
360 end
361 for k in pairs(from) do
362 if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then
363 if not list2[to[k]] then
364 deepTransfer(to[k], from[k], nil, major, list, list2)
365 end
366 to[k] = list[to[k]] or list2[to[k]]
367 else
368 rawset(to, k, from[k])
369 end
370 end
371 table_setn(to, table.getn(from))
372 setmetatable(to, getmetatable(from))
373 local mt = getmetatable(to)
374 if mt then
375 if list[mt] then
376 setmetatable(to, list[mt])
377 elseif mt.__index and list[mt.__index] then
378 mt.__index = list[mt.__index]
379 end
380 end
381 destroyTable(from)
382 if createdList then
383 del(list)
384 del(list2)
385 end
386 end
387 end
388  
389 -- @method IsNewVersion
390 -- @brief Obtain whether the supplied version would be an upgrade to the
391 -- current version. This allows for bypass code in library
392 -- declaration.
393 -- @param major A string representing the major version
394 -- @param minor An integer or an svn revision string representing the minor version
395 -- @return whether the supplied version would be newer than what is
396 -- currently available.
397 function AceLibrary:IsNewVersion(major, minor)
398 argCheck(self, major, 2, "string")
399 if type(minor) == "string" then
400 local m = svnRevisionToNumber(minor)
401 if m then
402 minor = m
403 else
404 _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
405 end
406 end
407 argCheck(self, minor, 3, "number")
408 local data = self.libs[major]
409 if not data then
410 return true
411 end
412 return data.minor < minor
413 end
414  
415 -- @method HasInstance
416 -- @brief Returns whether an instance exists. This allows for optional support of a library.
417 -- @param major A string representing the major version.
418 -- @param minor (optional) An integer or an svn revision string representing the minor version.
419 -- @return Whether an instance exists.
420 function AceLibrary:HasInstance(major, minor)
421 argCheck(self, major, 2, "string")
422 if minor then
423 if type(minor) == "string" then
424 local m = svnRevisionToNumber(minor)
425 if m then
426 minor = m
427 else
428 _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
429 end
430 end
431 argCheck(self, minor, 3, "number")
432 if not self.libs[major] then
433 return
434 end
435 return self.libs[major].minor == minor
436 end
437 return self.libs[major] and true
438 end
439  
440 -- @method GetInstance
441 -- @brief Returns the library with the given major/minor version.
442 -- @param major A string representing the major version.
443 -- @param minor (optional) An integer or an svn revision string representing the minor version.
444 -- @return The library with the given major/minor version.
445 function AceLibrary:GetInstance(major, minor)
446 argCheck(self, major, 2, "string")
447  
448 local data = self.libs[major]
449 if not data then
450 _G.error(string.format("Cannot find a library instance of %s.", major), 2)
451 return
452 end
453 if minor then
454 if type(minor) == "string" then
455 local m = svnRevisionToNumber(minor)
456 if m then
457 minor = m
458 else
459 _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
460 end
461 end
462 argCheck(self, minor, 2, "number")
463 if data.minor ~= minor then
464 _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2)
465 return
466 end
467 end
468 return data.instance
469 end
470  
471 -- Syntax sugar. AceLibrary("FooBar-1.0")
472 AceLibrary_mt.__call = AceLibrary.GetInstance
473  
474 local donothing
475  
476 local AceEvent
477  
478 -- @method Register
479 -- @brief Registers a new version of a given library.
480 -- @param newInstance the library to register
481 -- @param major the major version of the library
482 -- @param minor the minor version of the library
483 -- @param activateFunc (optional) A function to be called when the library is
484 -- fully activated. Takes the arguments
485 -- (newInstance [, oldInstance, oldDeactivateFunc]). If
486 -- oldInstance is given, you should probably call
487 -- oldDeactivateFunc(oldInstance).
488 -- @param deactivateFunc (optional) A function to be called by a newer library's
489 -- activateFunc.
490 -- @param externalFunc (optional) A function to be called whenever a new
491 -- library is registered.
492 function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc)
493 argCheck(self, newInstance, 2, "table")
494 argCheck(self, major, 3, "string")
495 if type(minor) == "string" then
496 local m = svnRevisionToNumber(minor)
497 if m then
498 minor = m
499 else
500 _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2)
501 end
502 end
503 argCheck(self, minor, 4, "number")
504 if math.floor(minor) ~= minor or minor < 0 then
505 error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor)
506 end
507 argCheck(self, activateFunc, 5, "function", "nil")
508 argCheck(self, deactivateFunc, 6, "function", "nil")
509 argCheck(self, externalFunc, 7, "function", "nil")
510 if not deactivateFunc then
511 if not donothing then
512 donothing = function() end
513 end
514 deactivateFunc = donothing
515 end
516 local data = self.libs[major]
517 if not data then
518 -- This is new
519 local instance = copyTable(newInstance)
520 crawlReplace(instance, instance, newInstance)
521 destroyTable(newInstance)
522 if AceLibrary == newInstance then
523 self = instance
524 AceLibrary = instance
525 end
526 self.libs[major] = {
527 instance = instance,
528 minor = minor,
529 deactivateFunc = deactivateFunc,
530 externalFunc = externalFunc,
531 }
532 rawset(instance, 'GetLibraryVersion', function(self)
533 return major, minor
534 end)
535 if not rawget(instance, 'error') then
536 rawset(instance, 'error', error)
537 end
538 if not rawget(instance, 'assert') then
539 rawset(instance, 'assert', assert)
540 end
541 if not rawget(instance, 'argCheck') then
542 rawset(instance, 'argCheck', argCheck)
543 end
544 if not rawget(instance, 'pcall') then
545 rawset(instance, 'pcall', pcall)
546 end
547 addToPositions(instance, major)
548 if activateFunc then
549 activateFunc(instance, nil, nil) -- no old version, so explicit nil
550 end
551  
552 if externalFunc then
553 for k,data in pairs(self.libs) do
554 if k ~= major then
555 externalFunc(instance, k, data.instance)
556 end
557 end
558 end
559  
560 for k,data in pairs(self.libs) do
561 if k ~= major and data.externalFunc then
562 data.externalFunc(data.instance, major, instance)
563 end
564 end
565 if major == "AceEvent-2.0" then
566 AceEvent = instance
567 end
568 if AceEvent then
569 AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance)
570 end
571  
572 return instance
573 end
574 local instance = data.instance
575 if minor <= data.minor then
576 -- This one is already obsolete, raise an error.
577 _G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2)
578 return
579 end
580 -- This is an update
581 local oldInstance = new()
582  
583 addToPositions(newInstance, major)
584 local isAceLibrary = (AceLibrary == newInstance)
585 local old_error, old_assert, old_argCheck, old_pcall
586 if isAceLibrary then
587 self = instance
588 AceLibrary = instance
589  
590 old_error = instance.error
591 old_assert = instance.assert
592 old_argCheck = instance.argCheck
593 old_pcall = instance.pcall
594  
595 self.error = error
596 self.assert = assert
597 self.argCheck = argCheck
598 self.pcall = pcall
599 end
600 deepTransfer(instance, newInstance, oldInstance, major)
601 crawlReplace(instance, instance, newInstance)
602 local oldDeactivateFunc = data.deactivateFunc
603 data.minor = minor
604 data.deactivateFunc = deactivateFunc
605 data.externalFunc = externalFunc
606 rawset(instance, 'GetLibraryVersion', function(self)
607 return major, minor
608 end)
609 if not rawget(instance, 'error') then
610 rawset(instance, 'error', error)
611 end
612 if not rawget(instance, 'assert') then
613 rawset(instance, 'assert', assert)
614 end
615 if not rawget(instance, 'argCheck') then
616 rawset(instance, 'argCheck', argCheck)
617 end
618 if not rawget(instance, 'pcall') then
619 rawset(instance, 'pcall', pcall)
620 end
621 if isAceLibrary then
622 for _,v in pairs(self.libs) do
623 local i = type(v) == "table" and v.instance
624 if type(i) == "table" then
625 if not rawget(i, 'error') or i.error == old_error then
626 rawset(i, 'error', error)
627 end
628 if not rawget(i, 'assert') or i.assert == old_assert then
629 rawset(i, 'assert', assert)
630 end
631 if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then
632 rawset(i, 'argCheck', argCheck)
633 end
634 if not rawget(i, 'pcall') or i.pcall == old_pcall then
635 rawset(i, 'pcall', pcall)
636 end
637 end
638 end
639 end
640 if activateFunc then
641 activateFunc(instance, oldInstance, oldDeactivateFunc)
642 else
643 oldDeactivateFunc(oldInstance)
644 end
645 del(oldInstance)
646  
647 if externalFunc then
648 for k,data in pairs(self.libs) do
649 if k ~= major then
650 externalFunc(instance, k, data.instance)
651 end
652 end
653 end
654  
655 return instance
656 end
657  
658 local iter
659 function AceLibrary:IterateLibraries()
660 if not iter then
661 local function iter(t, k)
662 k = next(t, k)
663 if not k then
664 return nil
665 else
666 return k, t[k].instance
667 end
668 end
669 end
670 return iter, self.libs, nil
671 end
672  
673 -- @function Activate
674 -- @brief The activateFunc for AceLibrary itself. Called when
675 -- AceLibrary properly registers.
676 -- @param self Reference to AceLibrary
677 -- @param oldLib (optional) Reference to an old version of AceLibrary
678 -- @param oldDeactivate (optional) Function to deactivate the old lib
679 local function activate(self, oldLib, oldDeactivate)
680 if not self.libs then
681 if oldLib then
682 self.libs = oldLib.libs
683 end
684 if not self.libs then
685 self.libs = {}
686 end
687 end
688 if not self.positions then
689 if oldLib then
690 self.positions = oldLib.positions
691 end
692 if not self.positions then
693 self.positions = setmetatable({}, { __mode = "k" })
694 end
695 end
696  
697 -- Expose the library in the global environment
698 _G[ACELIBRARY_MAJOR] = self
699  
700 if oldDeactivate then
701 oldDeactivate(oldLib)
702 end
703 end
704  
705 if not previous then
706 previous = AceLibrary
707 end
708 if not previous.libs then
709 previous.libs = {}
710 end
711 AceLibrary.libs = previous.libs
712 if not previous.positions then
713 previous.positions = setmetatable({}, { __mode = "k" })
714 end
715 AceLibrary.positions = previous.positions
716 AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate)