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
35Module moduleIndex[MODULE_HASH_SIZE] = { NULL };
36
37void
38listModules(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
47void
48dumpModules(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 */
68void
69mal_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
88static int getModuleIndex(str name) {
89 return (int) (strHash(name) % MODULE_HASH_SIZE);
90}
91
92static 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
110static void addModuleToIndex(Module cur){
111 int index = getModuleIndex(cur->name);
112 cur->link = moduleIndex[index];
113 moduleIndex[index] = cur;
114}
115
116Module 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
127void 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
153void 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 */
161Module 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 */
187Module 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 */
208Module 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 */
219static 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
240void 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 */
274void 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 */
327void 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 */
368Module 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 */
395Symbol 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
409Symbol findSymbol(Module usermodule, str mod, str fcn) {
410 Module m = findModule(usermodule, mod);
411 return findSymbolInModule(m, fcn);
412}
413
414