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