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 120816 : do {
49 120816 : if (s->kind != FUNCTIONsymbol) {
50 120775 : if (s->func && s->func->cname &&
51 120775 : 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 120805 : } 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 339 : mal_module_reset(void)
120 : {
121 339 : int i;
122 339 : Module m;
123 :
124 347475 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
125 347136 : m = moduleIndex[i];
126 347136 : moduleIndex[i] = 0;
127 370159 : while (m) {
128 23023 : Module next = m->link;
129 23023 : freeModule(m);
130 23023 : m = next;
131 : }
132 : }
133 339 : }
134 :
135 : static int
136 71504509 : getModuleIndex(const char *name)
137 : {
138 71504509 : return (int) (strHash(name) % MODULE_HASH_SIZE);
139 : }
140 :
141 : static void
142 23023 : clrModuleIndex(Module cur)
143 : {
144 23023 : int index = getModuleIndex(cur->name);
145 23023 : Module prev = NULL;
146 23023 : Module m = moduleIndex[index];
147 23023 : 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 23151 : addModuleToIndex(Module cur)
163 : {
164 23151 : int index = getModuleIndex(cur->name);
165 23151 : cur->link = moduleIndex[index];
166 23151 : moduleIndex[index] = cur;
167 23151 : }
168 :
169 : Module
170 71458335 : getModule(const char *name)
171 : {
172 71458335 : int index = getModuleIndex(name);
173 71458335 : Module m = moduleIndex[index];
174 71459375 : while (m) {
175 71441394 : if (name == m->name)
176 71440354 : return m;
177 1040 : m = m->link;
178 : }
179 : return NULL;
180 : }
181 :
182 : void
183 12 : getModuleList(Module **out, int *length)
184 : {
185 12 : int i;
186 12 : int moduleCount = 0;
187 12 : int currentIndex = 0;
188 12300 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
189 12288 : Module m = moduleIndex[i];
190 13110 : while (m) {
191 822 : moduleCount++;
192 822 : m = m->link;
193 : }
194 : }
195 12 : *out = GDKzalloc(moduleCount * sizeof(Module));
196 12 : if (*out == NULL) {
197 : return;
198 : }
199 12 : *length = moduleCount;
200 :
201 12300 : for (i = 0; i < MODULE_HASH_SIZE; i++) {
202 12288 : Module m = moduleIndex[i];
203 13110 : while (m) {
204 822 : (*out)[currentIndex++] = m;
205 822 : m = m->link;
206 : }
207 : }
208 : }
209 :
210 : void
211 12 : freeModuleList(Module *list)
212 : {
213 12 : GDKfree(list);
214 12 : }
215 :
216 : /*
217 : * Module scope management
218 : * It will contain the symbol table of all globally accessible functions.
219 : */
220 : Module
221 23151 : globalModule(const char *nme)
222 : {
223 23151 : Module cur;
224 :
225 : // Global modules are not named 'user'
226 23151 : assert(strcmp(nme, "user"));
227 23151 : nme = putName(nme);
228 23151 : if (nme == NULL)
229 : return NULL;
230 23151 : cur = (Module) GDKzalloc(sizeof(ModuleRecord));
231 23151 : if (cur == NULL)
232 : return NULL;
233 23151 : cur->name = nme;
234 23151 : cur->link = NULL;
235 23151 : addModuleToIndex(cur);
236 23151 : return cur;
237 : }
238 :
239 : /* Every client record has a private module name 'user'
240 : * for keeping around non-shared functions */
241 : Module
242 38338 : userModule(void)
243 : {
244 38338 : Module cur;
245 :
246 38338 : cur = (Module) GDKzalloc(sizeof(ModuleRecord));
247 38338 : if (cur == NULL)
248 : return NULL;
249 38338 : cur->name = putName("user");
250 38339 : if (cur->name == NULL) {
251 0 : GDKfree(cur);
252 0 : return NULL;
253 : }
254 38339 : cur->link = NULL;
255 38339 : 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 61360 : freeSubScope(Module scope)
279 : {
280 61360 : int i;
281 61360 : Symbol s;
282 :
283 15768477 : for (i = 0; i < MAXSCOPE; i++) {
284 15707117 : if (scope->space[i]) {
285 202517 : s = scope->space[i];
286 202517 : scope->space[i] = NULL;
287 202517 : freeSymbolList(s);
288 : }
289 : }
290 61360 : }
291 :
292 : void
293 61361 : freeModule(Module m)
294 : {
295 61361 : Symbol s;
296 :
297 61361 : if (m == NULL)
298 : return;
299 61361 : if ((s = findSymbolInModule(m, "epilogue")) != NULL) {
300 2361 : if (s->kind == COMMANDsymbol && s->func->argc <= 1 /* zero or one arg */) {
301 2361 : int status = 0;
302 2361 : str ret = MAL_SUCCEED;
303 :
304 2361 : ret = (*(str (*)(int *)) s->func->imp) (&status);
305 2361 : freeException(ret);
306 2361 : (void) status;
307 : }
308 : }
309 61360 : freeSubScope(m);
310 61361 : if (strcmp(m->name, "user")) {
311 23023 : clrModuleIndex(m);
312 : }
313 61361 : if (m->help)
314 0 : GDKfree(m->help);
315 61361 : 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 3517890 : insertSymbol(Module scope, Symbol prg)
326 : {
327 3517890 : int t;
328 :
329 3517890 : assert(scope);
330 3517890 : t = getSymbolIndex(prg->name);
331 3517890 : if (scope->space[t] != prg) {
332 3517880 : prg->peer = scope->space[t];
333 3517880 : scope->space[t] = prg;
334 3517880 : if (prg->peer && idcmp(prg->name, prg->peer->name) == 0)
335 2916466 : prg->skip = prg->peer->skip;
336 : else
337 601414 : prg->skip = prg->peer;
338 : }
339 3517890 : assert(prg != prg->peer);
340 3517890 : }
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 507 : deleteSymbol(Module scope, Symbol prg)
351 : {
352 507 : InstrPtr sig;
353 507 : int t;
354 :
355 507 : sig = getSignature(prg);
356 507 : 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 507 : scope = c;
362 : }
363 507 : t = getSymbolIndex(getFunctionId(sig));
364 507 : if (scope->space[t] == prg) {
365 482 : scope->space[t] = scope->space[t]->peer;
366 482 : freeSymbol(prg);
367 : } else {
368 : Symbol nxt = scope->space[t];
369 49 : while (nxt->peer != NULL) {
370 49 : if (nxt->peer == prg) {
371 25 : nxt->peer = prg->peer;
372 25 : nxt->skip = prg->peer;
373 25 : freeSymbol(prg);
374 25 : 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 67834952 : findModule(Module scope, const char *name)
391 : {
392 67834952 : Module def = scope;
393 67834952 : Module m;
394 67834952 : if (name == NULL)
395 : return scope;
396 67834952 : m = getModule(name);
397 67852615 : if (m)
398 : return m;
399 :
400 : /* default is always matched with current */
401 7506 : 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 591047 : findSymbolInModule(Module v, const char *fcn)
419 : {
420 591047 : Symbol s;
421 591047 : if (v == NULL || fcn == NULL)
422 : return NULL;
423 591030 : s = v->space[(int) (*fcn)];
424 1555341 : while (s != NULL) {
425 1484280 : if (idcmp(s->name, fcn) == 0)
426 519969 : return s;
427 964311 : 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 : }
|