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