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, 2025 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 204151 : for (int j = 0; j < MAXSCOPE; j++) {
46 203359 : Symbol s;
47 203359 : if ((s = moduleIndex[i]->space[j]) != NULL) {
48 120462 : do {
49 120462 : if (s->kind != FUNCTIONsymbol) {
50 120421 : if (s->func && s->func->cname &&
51 120421 : 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 120451 : } 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 355 : mal_module_reset(void)
120 : {
121 355 : int i;
122 355 : Module m;
123 :
124 363875 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
125 363520 : m = moduleIndex[i];
126 363520 : moduleIndex[i] = 0;
127 386913 : while (m) {
128 23393 : Module next = m->link;
129 23393 : freeModule(m);
130 23393 : m = next;
131 : }
132 : }
133 355 : }
134 :
135 : static int
136 64673271 : getModuleIndex(const char *name)
137 : {
138 64673271 : return (int) (strHash(name) % MODULE_HASH_SIZE);
139 : }
140 :
141 : static void
142 23393 : clrModuleIndex(Module cur)
143 : {
144 23393 : int index = getModuleIndex(cur->name);
145 23393 : Module prev = NULL;
146 23393 : Module m = moduleIndex[index];
147 23393 : 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 23516 : addModuleToIndex(Module cur)
163 : {
164 23516 : int index = getModuleIndex(cur->name);
165 23516 : cur->link = moduleIndex[index];
166 23516 : moduleIndex[index] = cur;
167 23516 : }
168 :
169 : Module
170 64626362 : getModule(const char *name)
171 : {
172 64627450 : for (Module m = moduleIndex[getModuleIndex(name)]; m; m = m->link) {
173 64591207 : if (name == m->name)
174 64590119 : return m;
175 : }
176 : return NULL;
177 : }
178 :
179 : void
180 16 : getModuleList(Module **out, int *length)
181 : {
182 16 : int i;
183 16 : int moduleCount = 0;
184 16 : int currentIndex = 0;
185 16400 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
186 16384 : Module m = moduleIndex[i];
187 17446 : while (m) {
188 1062 : moduleCount++;
189 1062 : m = m->link;
190 : }
191 : }
192 16 : *out = GDKzalloc(moduleCount * sizeof(Module));
193 16 : if (*out == NULL) {
194 : return;
195 : }
196 16 : *length = moduleCount;
197 :
198 16400 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
199 16384 : Module m = moduleIndex[i];
200 17446 : while (m) {
201 1062 : (*out)[currentIndex++] = m;
202 1062 : m = m->link;
203 : }
204 : }
205 : }
206 :
207 : void
208 16 : freeModuleList(Module *list)
209 : {
210 16 : GDKfree(list);
211 16 : }
212 :
213 : /*
214 : * Module scope management
215 : * It will contain the symbol table of all globally accessible functions.
216 : */
217 : Module
218 23516 : globalModule(const char *nme)
219 : {
220 23516 : Module cur;
221 :
222 : // Global modules are not named 'user'
223 23516 : assert(strcmp(nme, userRef));
224 23516 : nme = putName(nme);
225 23516 : if (nme == NULL)
226 : return NULL;
227 23516 : cur = (Module) GDKmalloc(sizeof(ModuleRecord));
228 23516 : if (cur == NULL)
229 : return NULL;
230 23516 : *cur = (ModuleRecord) {
231 : .name = nme,
232 : };
233 23516 : addModuleToIndex(cur);
234 23516 : return cur;
235 : }
236 :
237 : /* Every client record has a private module name 'user'
238 : * for keeping around non-shared functions */
239 : Module
240 37943 : userModule(void)
241 : {
242 37943 : Module cur;
243 :
244 37943 : cur = (Module) GDKmalloc(sizeof(ModuleRecord));
245 37943 : if (cur == NULL)
246 : return NULL;
247 37943 : *cur = (ModuleRecord) {
248 : .name = userRef,
249 : .link = NULL,
250 : };
251 37943 : return cur;
252 : }
253 :
254 : /*
255 : * The scope can be fixed. This is used by the parser.
256 : * Reading a module often calls for creation first.
257 : */
258 : Module
259 8 : fixModule(const char *nme)
260 : {
261 8 : Module m;
262 :
263 8 : m = getModule(nme);
264 8 : if (m)
265 : return m;
266 4 : return globalModule(nme);
267 : }
268 :
269 : /*
270 : * The freeModule operation throws away a symbol without
271 : * concerns on it whereabouts in the scope structure.
272 : */
273 : static void
274 61335 : freeSubScope(Module scope)
275 : {
276 61335 : int i;
277 61335 : Symbol s;
278 :
279 15762907 : for (i = 0; i < MAXSCOPE; i++) {
280 15701572 : if (scope->space[i]) {
281 204536 : s = scope->space[i];
282 204536 : scope->space[i] = NULL;
283 204536 : freeSymbolList(s);
284 : }
285 : }
286 61335 : }
287 :
288 : void
289 61335 : freeModule(Module m)
290 : {
291 61335 : Symbol s;
292 :
293 61335 : if (m == NULL)
294 : return;
295 61335 : if ((s = findSymbolInModule(m, "epilogue")) != NULL) {
296 2463 : if (s->kind == COMMANDsymbol && s->func->argc <= 1 /* zero or one arg */) {
297 2463 : int status = 0;
298 2463 : str ret;
299 :
300 2463 : TRC_INFO(MAL_LOADER, "Unloading module %s\n", m->name);
301 2463 : ret = (*(str (*)(int *)) s->func->imp) (&status);
302 2463 : freeException(ret);
303 2463 : (void) status;
304 : }
305 : }
306 61335 : freeSubScope(m);
307 61335 : if (strcmp(m->name, userRef)) {
308 23393 : clrModuleIndex(m);
309 : }
310 61335 : if (m->help)
311 0 : GDKfree(m->help);
312 61335 : GDKfree(m);
313 : }
314 :
315 : /*
316 : * After filling in a structure it is added to the multi-level symbol
317 : * table. We keep a skip list of similarly named function symbols.
318 : * This speeds up searching provided the modules adhere to the
319 : * structure and group the functions as well.
320 : */
321 : void
322 3669920 : insertSymbol(Module scope, Symbol prg)
323 : {
324 3669920 : int t;
325 :
326 3669920 : assert(scope);
327 3669920 : t = getSymbolIndex(prg->name);
328 3669920 : if (scope->space[t] != prg) {
329 3669910 : prg->peer = scope->space[t];
330 3669910 : scope->space[t] = prg;
331 3669910 : if (prg->peer && idcmp(prg->name, prg->peer->name) == 0)
332 3052718 : prg->skip = prg->peer->skip;
333 : else
334 617192 : prg->skip = prg->peer;
335 : }
336 3669920 : assert(prg != prg->peer);
337 3669920 : }
338 :
339 : /*
340 : * Removal of elements from the symbol table should be
341 : * done with care. For, it should be assured that
342 : * there are no references to the definition at the
343 : * moment of removal. This situation can not easily
344 : * checked at runtime, without tremendous overhead.
345 : */
346 : void
347 513 : deleteSymbol(Module scope, Symbol prg)
348 : {
349 513 : InstrPtr sig;
350 513 : int t;
351 :
352 513 : sig = getSignature(prg);
353 513 : if (getModuleId(sig) && getModuleId(sig) != scope->name) {
354 : /* move the definition to the proper place */
355 : /* default scope is the last resort */
356 0 : Module c = findModule(scope, getModuleId(sig));
357 0 : if (c)
358 513 : scope = c;
359 : }
360 513 : t = getSymbolIndex(getFunctionId(sig));
361 513 : if (scope->space[t] == prg) {
362 487 : scope->space[t] = scope->space[t]->peer;
363 487 : freeSymbol(prg);
364 : } else {
365 : Symbol nxt = scope->space[t];
366 50 : while (nxt->peer != NULL) {
367 50 : if (nxt->peer == prg) {
368 26 : nxt->peer = prg->peer;
369 26 : nxt->skip = prg->peer;
370 26 : freeSymbol(prg);
371 26 : return;
372 : }
373 : nxt = nxt->peer;
374 : }
375 : }
376 : }
377 :
378 : /*
379 : * Searching the scope structure.
380 : * Finding a scope is unrestricted. For modules we explicitly look for
381 : * the start of a new module scope.
382 : * All core modules are accessed through the jumptable.
383 : * The 'user' module is an alias for the scope attached
384 : * to the current user.
385 : */
386 : Module
387 60796191 : findModule(Module scope, const char *name)
388 : {
389 60796191 : Module def = scope;
390 60796191 : Module m;
391 60796191 : if (name == NULL)
392 : return scope;
393 60796191 : m = getModule(name);
394 60795364 : if (m)
395 : return m;
396 :
397 : /* default is always matched with current */
398 7561 : if (def->name == NULL)
399 : return NULL;
400 : return def;
401 : }
402 :
403 : /*
404 : * The routine findSymbolInModule starts at a MAL scope level and searches
405 : * an element amongst the peers.
406 : *
407 : * In principal, external variables are subject to synchronization actions
408 : * to avoid concurrency conflicts. This also implies, that any parallel
409 : * block introduces a temporary scope.
410 : *
411 : * The variation on this routine is to dump the definition of
412 : * all matching definitions.
413 : */
414 : Symbol
415 662670 : findSymbolInModule(Module v, const char *fcn)
416 : {
417 662670 : Symbol s;
418 662670 : if (v == NULL || fcn == NULL)
419 : return NULL;
420 662653 : s = v->space[(int) (*fcn)];
421 1690229 : while (s != NULL) {
422 1618739 : if (idcmp(s->name, fcn) == 0)
423 591163 : return s;
424 1027576 : s = s->skip;
425 : }
426 : return NULL;
427 : }
428 :
429 : Symbol
430 91 : findSymbol(Module usermodule, const char *mod, const char *fcn)
431 : {
432 91 : Module m = findModule(usermodule, mod);
433 91 : return findSymbolInModule(m, fcn);
434 : }
|