1 | /* |
2 | * This Source Code Form is subject to the terms of the Mozilla Public |
3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
5 | * |
6 | * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V. |
7 | */ |
8 | |
9 | /* |
10 | * (author) M. L. Kersten |
11 | * All symbols are collected in modules. Modules are either global |
12 | * or private for the user. The latter are known as 'user' module functions |
13 | * and reside within the Client record. |
14 | */ |
15 | |
16 | #include "monetdb_config.h" |
17 | #include "mal_module.h" |
18 | #include "mal_function.h" /* for printFunction() */ |
19 | #include "mal_namespace.h" |
20 | #include "mal_client.h" |
21 | #include "mal_interpreter.h" |
22 | #include "mal_listing.h" |
23 | #include "mal_private.h" |
24 | |
25 | /* |
26 | * Definition of a new module may interfere with concurrent actions. |
27 | * A jump table is mainted to provide a quick start in the module |
28 | * table to find the correct one. |
29 | * |
30 | * All modules are persistent during a server session |
31 | */ |
32 | /* #define _DEBUG_MODULE_*/ |
33 | |
34 | #define MODULE_HASH_SIZE 1024 |
35 | Module moduleIndex[MODULE_HASH_SIZE] = { NULL }; |
36 | |
37 | void |
38 | listModules(stream *out, Module s) |
39 | { |
40 | while(s){ |
41 | mnstr_printf(out,"Unexpected module %s\n" , s->name); |
42 | s= s->link; |
43 | } |
44 | } |
45 | |
46 | // perform sanity check on duplicate occurrences as well |
47 | void |
48 | dumpModules(stream *out) |
49 | { |
50 | int i; |
51 | Module s,n; |
52 | for( i = 0; i< MODULE_HASH_SIZE; i++){ |
53 | s= moduleIndex[i]; |
54 | while(s){ |
55 | mnstr_printf(out,"[%d] module %s\n" , i, s->name); |
56 | n = s->link; |
57 | while(n){ |
58 | if( n == s) |
59 | mnstr_printf(out,"ASSERTION error, double occurrence of symbol in symbol table\n" ); |
60 | n= n->link; |
61 | } |
62 | s= s->link; |
63 | } |
64 | } |
65 | } |
66 | |
67 | /* Remove all globally known functions */ |
68 | void |
69 | mal_module_reset(void) |
70 | { |
71 | int i; |
72 | Module m; |
73 | |
74 | #ifdef _DEBUG_MODULE_ |
75 | fprintf(stderr,"#et the globale module structure \n" ); |
76 | #endif |
77 | for(i = 0; i < MODULE_HASH_SIZE; i++) { |
78 | m= moduleIndex[i]; |
79 | moduleIndex[i] = 0; |
80 | while(m) { |
81 | Module next = m->link; |
82 | freeModule(m); |
83 | m = next; |
84 | } |
85 | } |
86 | } |
87 | |
88 | static int getModuleIndex(str name) { |
89 | return (int) (strHash(name) % MODULE_HASH_SIZE); |
90 | } |
91 | |
92 | static void clrModuleIndex(Module cur){ |
93 | int index = getModuleIndex(cur->name); |
94 | Module prev = NULL; |
95 | Module m = moduleIndex[index]; |
96 | while(m) { |
97 | if (m == cur) { |
98 | if (!prev) { |
99 | moduleIndex[index] = m->link; |
100 | } else { |
101 | prev->link = m->link; |
102 | } |
103 | return; |
104 | } |
105 | prev = m; |
106 | m = m->link; |
107 | } |
108 | } |
109 | |
110 | static void addModuleToIndex(Module cur){ |
111 | int index = getModuleIndex(cur->name); |
112 | cur->link = moduleIndex[index]; |
113 | moduleIndex[index] = cur; |
114 | } |
115 | |
116 | Module getModule(str name) { |
117 | int index = getModuleIndex(name); |
118 | Module m = moduleIndex[index]; |
119 | while(m) { |
120 | if (name == m->name) |
121 | return m; |
122 | m = m->link; |
123 | } |
124 | return NULL; |
125 | } |
126 | |
127 | void getModuleList(Module** out, int* length) { |
128 | int i; |
129 | int moduleCount = 0; |
130 | int currentIndex = 0; |
131 | for(i = 0; i < MODULE_HASH_SIZE; i++) { |
132 | Module m = moduleIndex[i]; |
133 | while(m) { |
134 | moduleCount++; |
135 | m = m->link; |
136 | } |
137 | } |
138 | *out = GDKzalloc(moduleCount * sizeof(Module)); |
139 | if (*out == NULL) { |
140 | return; |
141 | } |
142 | *length = moduleCount; |
143 | |
144 | for(i = 0; i < MODULE_HASH_SIZE; i++) { |
145 | Module m = moduleIndex[i]; |
146 | while(m) { |
147 | (*out)[currentIndex++] = m; |
148 | m = m->link; |
149 | } |
150 | } |
151 | } |
152 | |
153 | void freeModuleList(Module* list) { |
154 | GDKfree(list); |
155 | } |
156 | |
157 | /* |
158 | * Module scope management |
159 | * It will contain the symbol table of all globally accessible functions. |
160 | */ |
161 | Module globalModule(str nme) |
162 | { Module cur; |
163 | |
164 | // Global modules are not named 'user' |
165 | assert (strcmp(nme, "user" )); |
166 | #ifdef _DEBUG_MODULE_ |
167 | fprintf(stderr,"#create new global module %s\n" ,nme); |
168 | #endif |
169 | nme = putName(nme); |
170 | cur = (Module) GDKzalloc(sizeof(ModuleRecord)); |
171 | if (cur == NULL) |
172 | return NULL; |
173 | cur->name = nme; |
174 | cur->link = NULL; |
175 | cur->isAtomModule = FALSE; |
176 | cur->space = (Symbol *) GDKzalloc(MAXSCOPE * sizeof(Symbol)); |
177 | if (cur->space == NULL) { |
178 | GDKfree(cur); |
179 | return NULL; |
180 | } |
181 | addModuleToIndex(cur); |
182 | return cur; |
183 | } |
184 | |
185 | /* Every client record has a private module name 'user' |
186 | * for keeping around non-shared functions */ |
187 | Module userModule(void){ |
188 | Module cur; |
189 | |
190 | cur = (Module) GDKzalloc(sizeof(ModuleRecord)); |
191 | if (cur == NULL) |
192 | return NULL; |
193 | cur->name = putName("user" ); |
194 | cur->link = NULL; |
195 | cur->space = NULL; |
196 | cur->isAtomModule = FALSE; |
197 | cur->space = (Symbol *) GDKzalloc(MAXSCOPE * sizeof(Symbol)); |
198 | if (cur->space == NULL) { |
199 | GDKfree(cur); |
200 | return NULL; |
201 | } |
202 | return cur; |
203 | } |
204 | /* |
205 | * The scope can be fixed. This is used by the parser. |
206 | * Reading a module often calls for creation first. |
207 | */ |
208 | Module fixModule(str nme) { |
209 | Module m; |
210 | |
211 | m = getModule(nme); |
212 | if (m) return m; |
213 | return globalModule(nme); |
214 | } |
215 | /* |
216 | * The freeModule operation throws away a symbol without |
217 | * concerns on it whereabouts in the scope structure. |
218 | */ |
219 | static void freeSubScope(Module scope) |
220 | { |
221 | int i; |
222 | Symbol s; |
223 | |
224 | if (scope->space == NULL) |
225 | return; |
226 | #ifdef _DEBUG_MODULE_ |
227 | fprintf(stderr,"#freeSubScope %s \n" , scope->name); |
228 | #endif |
229 | for(i = 0; i < MAXSCOPE; i++) { |
230 | if( scope->space[i]){ |
231 | s= scope->space[i]; |
232 | scope->space[i] = NULL; |
233 | freeSymbolList(s); |
234 | } |
235 | } |
236 | GDKfree(scope->space); |
237 | scope->space = 0; |
238 | } |
239 | |
240 | void freeModule(Module m) |
241 | { |
242 | Symbol s; |
243 | |
244 | if (m == NULL) |
245 | return; |
246 | if ((s = findSymbolInModule(m, "epilogue" )) != NULL) { |
247 | InstrPtr pci = getInstrPtr(s->def,0); |
248 | if (pci && pci->token == COMMANDsymbol && pci->argc == 1) { |
249 | int ret = 0; |
250 | |
251 | assert(pci->fcn != NULL); |
252 | (*pci->fcn)(&ret); |
253 | (void)ret; |
254 | } |
255 | } |
256 | #ifdef _DEBUG_MODULE_ |
257 | fprintf(stderr,"#freeModue %s \n" , m->name); |
258 | #endif |
259 | freeSubScope(m); |
260 | if (strcmp(m->name, "user" )) { |
261 | clrModuleIndex(m); |
262 | } |
263 | if (m->help) |
264 | GDKfree(m->help); |
265 | GDKfree(m); |
266 | } |
267 | |
268 | /* |
269 | * After filling in a structure it is added to the multi-level symbol |
270 | * table. We keep a skip list of similarly named function symbols. |
271 | * This speeds up searching provided the modules adhere to the |
272 | * structure and group the functions as well. |
273 | */ |
274 | void insertSymbol(Module scope, Symbol prg){ |
275 | InstrPtr sig; |
276 | int t; |
277 | Module c; |
278 | |
279 | assert(scope); |
280 | sig = getSignature(prg); |
281 | #ifdef _DEBUG_MODULE_ |
282 | fprintf(stderr,"#insertSymbol: %s.%s in %s " , getModuleId(sig), getFunctionId(sig), scope->name); |
283 | #endif |
284 | if(getModuleId(sig) && getModuleId(sig)!= scope->name){ |
285 | /* move the definition to the proper place */ |
286 | /* default scope is the last resort */ |
287 | c= findModule(scope,getModuleId(sig)); |
288 | if ( c ) |
289 | scope = c; |
290 | #ifdef _DEBUG_MODULE_ |
291 | fprintf(stderr," found alternative module %s " , scope->name); |
292 | #endif |
293 | } |
294 | t = getSymbolIndex(getFunctionId(sig)); |
295 | if( scope->space == NULL) { |
296 | scope->space = (Symbol *) GDKzalloc(MAXSCOPE * sizeof(Symbol)); |
297 | if (scope->space == NULL) |
298 | return; |
299 | } |
300 | assert(scope->space); |
301 | if (scope->space[t] == prg){ |
302 | /* already known, last inserted */ |
303 | #ifdef _DEBUG_MODULE_ |
304 | fprintf(stderr," unexpected double insert " ); |
305 | #endif |
306 | } else { |
307 | prg->peer= scope->space[t]; |
308 | scope->space[t] = prg; |
309 | if( prg->peer && |
310 | idcmp(prg->name,prg->peer->name) == 0) |
311 | prg->skip = prg->peer->skip; |
312 | else |
313 | prg->skip = prg->peer; |
314 | } |
315 | assert(prg != prg->peer); |
316 | #ifdef _DEBUG_MODULE_ |
317 | fprintf(stderr,"\n" ); |
318 | #endif |
319 | } |
320 | /* |
321 | * Removal of elements from the symbol table should be |
322 | * done with care. For, it should be assured that |
323 | * there are no references to the definition at the |
324 | * moment of removal. This situation can not easily |
325 | * checked at runtime, without tremendous overhead. |
326 | */ |
327 | void deleteSymbol(Module scope, Symbol prg){ |
328 | InstrPtr sig; |
329 | int t; |
330 | |
331 | sig = getSignature(prg); |
332 | #ifdef _DEBUG_MODULE_ |
333 | fprintf(stderr,"#delete symbol %s.%s from %s\n" , getModuleId(sig), getFunctionId(sig), prg->name); |
334 | #endif |
335 | if (getModuleId(sig) && getModuleId(sig)!= scope->name ){ |
336 | /* move the definition to the proper place */ |
337 | /* default scope is the last resort */ |
338 | Module c= findModule(scope, getModuleId(sig)); |
339 | if(c ) |
340 | scope = c; |
341 | } |
342 | t = getSymbolIndex(getFunctionId(sig)); |
343 | if (scope->space[t] == prg) { |
344 | scope->space[t] = scope->space[t]->peer; |
345 | freeSymbol(prg); |
346 | } else { |
347 | Symbol nxt = scope->space[t]; |
348 | while (nxt->peer != NULL) { |
349 | if (nxt->peer == prg) { |
350 | nxt->peer = prg->peer; |
351 | nxt->skip = prg->peer; |
352 | freeSymbol(prg); |
353 | return; |
354 | } |
355 | nxt = nxt->peer; |
356 | } |
357 | } |
358 | } |
359 | |
360 | /* |
361 | * Searching the scope structure. |
362 | * Finding a scope is unrestricted. For modules we explicitly look for |
363 | * the start of a new module scope. |
364 | * All core modules are accessed through the jumptable. |
365 | * The 'user' module is an alias for the scope attached |
366 | * to the current user. |
367 | */ |
368 | Module findModule(Module scope, str name){ |
369 | Module def = scope; |
370 | Module m; |
371 | if (name == NULL) return scope; |
372 | |
373 | #ifdef _DEBUG_MODULE_ |
374 | fprintf(stderr,"Locate module %s in scope %s\n" , name,scope->name); |
375 | #endif |
376 | m = getModule(name); |
377 | if (m) return m; |
378 | |
379 | /* default is always matched with current */ |
380 | if (def->name == NULL) return NULL; |
381 | return def; |
382 | } |
383 | |
384 | /* |
385 | * The routine findSymbolInModule starts at a MAL scope level and searches |
386 | * an element amongst the peers. |
387 | * |
388 | * In principal, external variables are subject to synchronization actions |
389 | * to avoid concurrency conflicts. This also implies, that any parallel |
390 | * block introduces a temporary scope. |
391 | * |
392 | * The variation on this routine is to dump the definition of |
393 | * all matching definitions. |
394 | */ |
395 | Symbol findSymbolInModule(Module v, str fcn) { |
396 | Symbol s; |
397 | if (v == NULL || fcn == NULL) return NULL; |
398 | #ifdef _DEBUG_MODULE_ |
399 | fprintf(stderr,"#find symbol %s in %s\n" , fcn, v->name); |
400 | #endif |
401 | s = v->space[(int)(*fcn)]; |
402 | while (s != NULL) { |
403 | if (idcmp(s->name,fcn)==0) return s; |
404 | s = s->skip; |
405 | } |
406 | return NULL; |
407 | } |
408 | |
409 | Symbol findSymbol(Module usermodule, str mod, str fcn) { |
410 | Module m = findModule(usermodule, mod); |
411 | return findSymbolInModule(m, fcn); |
412 | } |
413 | |
414 | |