1if not modules then modules = { } end modules ['luat-cbk'] = {
2 version = 1.001,
3 comment = "companion to luat-lib.mkiv",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local insert, remove, concat = table.insert, table.remove, table.concat
10local find, format = string.find, string.format
11local collectgarbage, type, next = collectgarbage, type, next
12local round = math.round
13local sortedhash, sortedkeys, tohash = table.sortedhash, table.sortedkeys, table.tohash
14
15
16
17
18
19callbacks = callbacks or { }
20local callbacks = callbacks
21
22
23
24
25
26local trace_callbacks = false trackers.register("system.callbacks", function(v) trace_callbacks = v end)
27local trace_calls = false
28local trace_checking = false trackers.register("memory.checking", function(v) trace_checking = v end)
29
30local report_system = logs.reporter("system")
31local report_callbacks = logs.reporter("system","callbacks")
32local report_memory = logs.reporter("system","memory")
33
34local register_callback = callback.register
35local find_callback = callback.find
36local list_callbacks = callback.list
37local register_usercall = false
38local original_register = register_callback
39
40local frozen = { }
41local stack = { }
42local list = callbacks.list
43local permit_overloads = false
44local block_overloads = false
45
46
47
48
49
50
51
52
53
54
55directives.register("system.callbacks.permitoverloads", function(v)
56 if block_overloads or permit_overloads then
57
58 elseif v then
59 permit_overloads = { }
60 report_system()
61 report_system("The callback system has been brought in an unprotected state. As a result of directly")
62 report_system("setting of callbacks subsystems of ConTeXt can stop working. There is no support for")
63 report_system("bugs resulting from this state. It's better to use the official extension mechanisms.")
64 report_system()
65 end
66end)
67
68sandbox.initializer {
69 category = "functions",
70 action = function()
71 block_overloads = true
72 end
73}
74
75if not list then
76
77 list = utilities.storage.allocate(list_callbacks())
78
79 local supported = { }
80
81 for k in next, list do
82 list[k] = 0
83 supported[k] = true
84 end
85
86 callbacks.list = list
87 callbacks.supported = supported
88
89end
90
91local delayed = tohash {
92 "buildpage_filter",
93}
94
95if trace_calls then
96
97 local functions = { }
98
99 register_callback = function(name,func)
100 if type(func) == "function" then
101 if functions[name] then
102 functions[name] = func
103 return find_callback(name)
104 else
105 functions[name] = func
106 local cnuf = function(...)
107 list[name] = list[name] + 1
108 return functions[name](...)
109 end
110 return original_register(name,cnuf)
111 end
112 else
113 return original_register(name,func)
114 end
115 end
116
117end
118
119
120
121callbacks.functions = { }
122
123
124
125local reported = { }
126
127local function register_usercall(what,name,func)
128 if list[name] then
129 if trace_callbacks or not reported[name] then
130 report_system()
131 report_system("disabling core code by %s user function into callback '%s' (reported only once)",what,name)
132 report_system()
133 reported[name] = true
134 end
135 permit_overloads[name] = true
136 return original_register(name,function(...)
137 if trace_callbacks then
138 report_callbacks("calling user function from '%s'",name)
139 end
140 return func(...)
141 end)
142 else
143 report_callbacks("not %s function into invalid callback '%s'",name)
144 return nil, format("unknown callback '%s'",name)
145 end
146end
147
148local function frozen_callback(name)
149 report_callbacks("not %s frozen %a","registering",name)
150 return nil, format("callback '%s' is frozen",name)
151end
152
153local function state(name)
154 local f = find_callback(name)
155 if f == false then
156 return "disabled"
157 elseif f then
158 return "enabled"
159 else
160 return "undefined"
161 end
162end
163
164function callbacks.known(name)
165 return list[name]
166end
167
168function callbacks.report()
169 for name, _ in sortedhash(list) do
170 local str = frozen[name]
171 if str then
172 report_callbacks("%s: %s -> %s",state(name),name,str)
173 else
174 report_callbacks("%s: %s",state(name),name)
175 end
176 end
177end
178
179function callbacks.freeze(name,freeze)
180 if not permit_overloads then
181 freeze = type(freeze) == "string" and freeze
182 if find(name,"*",1,true) then
183 local pattern = name
184 for name, _ in next, list do
185 if find(name,pattern) then
186 frozen[name] = freeze or frozen[name] or "frozen"
187 end
188 end
189 else
190 frozen[name] = freeze or frozen[name] or "frozen"
191 end
192 end
193end
194
195function callbacks.register(name,func,freeze)
196 if frozen[name] then
197 if permit_overloads then
198 return register_usercall("registering",name,func)
199 else
200 return frozen_callback(name)
201 end
202 elseif freeze then
203 frozen[name] = type(freeze) == "string" and freeze or "registered"
204 end
205 if delayed[name] and environment.initex then
206 return nil
207 end
208 return register_callback(name,func)
209end
210
211function callback.register(name,func)
212 if not frozen[name] then
213 return register_callback(name,func)
214 elseif permit_overloads then
215 return register_usercall("registering",name,func)
216 else
217 return frozen_callback(name)
218 end
219end
220
221function callbacks.push(name,func)
222 if not frozen[name] or permit_overloads then
223 local sn = stack[name]
224 if not sn then
225 sn = { }
226 stack[name] = sn
227 end
228 insert(sn,find_callback(name))
229 if permit_overloads then
230 register_usercall("pushing",name,func)
231 else
232 register_callback(name,func)
233 end
234 else
235 report_callbacks("not %s frozen %a","pushing",name)
236 end
237end
238
239function callbacks.pop(name)
240 if not frozen[name] or permit_overloads then
241 local sn = stack[name]
242 if not sn or #sn == 0 then
243
244 register_callback(name,nil)
245 else
246
247 local func = remove(sn)
248 register_callback(name,func)
249 end
250 end
251end
252
253if trace_calls then
254 statistics.register("callback details", function()
255 local t = { }
256 for name, n in sortedhash(list) do
257 if n > 0 then
258 t[#t+1] = format("%s -> %s",name,n)
259 end
260 end
261 return concat(t," ")
262 end)
263end
264
265statistics.register("callbacks overloaded by user", function()
266 if permit_overloads then
267 return concat(sortedkeys(permit_overloads)," ")
268 end
269end)
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323utilities = utilities or { }
324utilities.garbagecollector = utilities.garbagecollector or { }
325local garbagecollector = utilities.garbagecollector
326
327garbagecollector.enabled = false
328garbagecollector.criterium = 4*1024*1024
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354function garbagecollector.check(size,criterium)
355 if garbagecollector.enabled then
356 criterium = criterium or garbagecollector.criterium
357 if not size or (criterium and criterium > 0 and size > criterium) then
358 if trace_checking then
359 local b = collectgarbage("count")
360 collectgarbage("collect")
361 local a = collectgarbage("count")
362 report_memory("forced sweep, collected: %s MB, used: %s MB",round((b-a)/1000),round(a/1000))
363 else
364 collectgarbage("collect")
365 end
366 end
367 end
368end
369
370
371
372commands = commands or { }
373
374function commands.showcallbacks()
375 local NC, NR, verbatim = context.NC, context.NR, context.type
376 context.starttabulate { "|l|l|p|" }
377 for name, _ in sortedhash(list) do
378 NC() verbatim(name) NC() verbatim(state(name)) NC() context(frozen[name] or "") NC() NR()
379 end
380 context.stoptabulate()
381end
382 |