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 type, next = 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 local usage = list_callbacks()
170 for name, _ in sortedhash(list) do
171 local str = frozen[name]
172 local sta = state(name)
173 local use = usage[name] and "+" or "-"
174 if str then
175 report_callbacks("%-9s : %s : %-22s -> %s",sta,use,name,str)
176 else
177 report_callbacks("%-9s : %s : %-22s", sta,use,name)
178 end
179 end
180end
181
182function callbacks.freeze(name,freeze)
183 if not permit_overloads then
184 freeze = type(freeze) == "string" and freeze
185 if find(name,"*",1,true) then
186 local pattern = name
187 for name, _ in next, list do
188 if find(name,pattern) then
189 frozen[name] = freeze or frozen[name] or "frozen"
190 end
191 end
192 else
193 frozen[name] = freeze or frozen[name] or "frozen"
194 end
195 end
196end
197
198function callbacks.register(name,func,freeze)
199 if frozen[name] then
200 if permit_overloads then
201 return register_usercall("registering",name,func)
202 else
203 return frozen_callback(name)
204 end
205 elseif freeze then
206 frozen[name] = type(freeze) == "string" and freeze or "registered"
207 end
208 if delayed[name] and environment.initex then
209 return nil
210 end
211 return register_callback(name,func)
212end
213
214function callback.register(name,func)
215 if not frozen[name] then
216 return register_callback(name,func)
217 elseif permit_overloads then
218 return register_usercall("registering",name,func)
219 else
220 return frozen_callback(name)
221 end
222end
223
224function callbacks.push(name,func)
225 if not frozen[name] or permit_overloads then
226 local sn = stack[name]
227 if not sn then
228 sn = { }
229 stack[name] = sn
230 end
231 insert(sn,find_callback(name))
232 if permit_overloads then
233 register_usercall("pushing",name,func)
234 else
235 register_callback(name,func)
236 end
237 else
238 report_callbacks("not %s frozen %a","pushing",name)
239 end
240end
241
242function callbacks.pop(name)
243 if not frozen[name] or permit_overloads then
244 local sn = stack[name]
245 if not sn or #sn == 0 then
246
247 register_callback(name,nil)
248 else
249
250 local func = remove(sn)
251 register_callback(name,func)
252 end
253 end
254end
255
256if trace_calls then
257 statistics.register("callback details", function()
258 local t = { }
259 for name, n in sortedhash(list) do
260 if n > 0 then
261 t[#t+1] = format("%s -> %s",name,n)
262 end
263 end
264 return concat(t," ")
265 end)
266end
267
268statistics.register("callbacks overloaded by user", function()
269 if permit_overloads then
270 return concat(sortedkeys(permit_overloads)," ")
271 end
272end)
273
274
275
276commands = commands or { }
277
278function commands.showcallbacks()
279 local NC, NR, verbatim = context.NC, context.NR, context.type
280 context.starttabulate { "|l|l|p|" }
281 for name, _ in sortedhash(list) do
282 NC() verbatim(name) NC() verbatim(state(name)) NC() context(frozen[name] or "") NC() NR()
283 end
284 context.stoptabulate()
285end
286 |