1 /*---------------------------------------------------------------------------
2 * Simul-Efun Handling
3 *
4 *---------------------------------------------------------------------------
5 * Simul-efuns are a way to provide a mudlib with additional efuns which are
6 * nevertheless implemented in LPC.
7 *
8 * The simul-efuns are implemented in one "simul-efun object" which is
9 * loaded by the master and made known to the driver by it's name (this way
10 * it is easier to find it again after an update). When a simul-efun is
11 * removed from the game, the master may provide 'backup' objects which
12 * still provide the old simul-efun so that older programs still run
13 * albeit slower (see interpret.c:call_simul_efun()). The semantic of
14 * a simul-efun call is that of a call-other, and specific calls may even
15 * be implemented as such.
16 *
17 * The driver keeps a table (simul_efunp) of all simul-efuns compiled so far,
18 * distinguished by name and number of arguments. If a simul-efun is removed
19 * from the simul-efun object, its corresponding entry is only marked as
20 * "discarded", again because older programs may still reference it by index.
21 * If such a discarded simul-efun is re-implemented by a new simul-efun object,
22 * the old table entry is reactivated.
23 *
24 * The first SEFUN_TABLE_SIZE simul-efuns of this table are mirrored in a
25 * second table (simul_efun_table) from where they are called by index with
26 * the special SIMUL_EFUN instruction.
27 *---------------------------------------------------------------------------
28 */
29
30 #include "driver.h"
31 #include "typedefs.h"
32
33 #include "my-alloca.h"
34 #include <stdio.h>
35
36 #include "simul_efun.h"
37
38 #include "array.h"
39 #include "exec.h"
40 #include "gcollect.h"
41 #include "interpret.h"
42 #include "lex.h"
43 #include "mstrings.h"
44 #include "object.h"
45 #include "ptrtable.h"
46 #include "prolang.h" /* is_undef_function() */
47 #include "simulate.h"
48 #include "stdstrings.h"
49 #include "svalue.h"
50 #include "swap.h"
51 #include "xalloc.h"
52
53 /*-------------------------------------------------------------------------*/
54
55 function_t *simul_efunp = NULL;
56 /* The function_t's of all simul_efuns encountered. sefuns are distinguished
57 * by name and number of arguments - discarded sefuns are not removed
58 * because older programs might still reference them. On the other hand,
59 * a discarded sefun may be re-actived if a suitable function is compiled.
60 */
61
62 object_t *simul_efun_object = NULL;
63 /* The primary simul_efun object.
64 * The pointer is not counted in the references, and explicitely
65 * check in simulate:destruct().
66 */
67
68 vector_t *simul_efun_vector = NULL;
69 /* When available, all simul_efun object names
70 * The first is the name of the primary object - it these object's
71 * functions which are tabled. All alternative objects are used
72 * using a normal apply() from interpret.c:call_simul_efun().
73 */
74
75 #define SIZE_SEFUN_TABLE (sizeof simul_efun_table / sizeof simul_efun_table[0])
76
77 simul_efun_table_t simul_efun_table[SEFUN_TABLE_SIZE];
78 /* The table holding the information for all simul-efuns which
79 * can be called directly with the SIMUL_EFUN instruction.
80 * A .funstart of NULL marks unused/discarded entries.
81 */
82
83 int num_simul_efun = 0;
84 /* Number of functions (active or not) listed in simul_efunp.
85 */
86
87 static int total_simul_efun = 0;
88 /* Allocated size of simul_efunp.
89 */
90
91 static string_t *simul_efun_file_name = NULL;
92 /* The shared name of the primary simul_efun object
93 */
94
95 static program_t *simul_efun_program= NULL;
96 /* The program of the primary simul_efun object.
97 */
98
99 static ident_t *all_simul_efuns = NULL;
100 /* The list of all active simul_efun identifiers, which are also
101 * held in the symbol table.
102 */
103
104 static short all_discarded_simul_efun = -1;
105 /* First index of the list of discarded sefuns in simul_efunp.
106 * With this list, it is faster to find a sefun entry to reactivate.
107 */
108
109 /*-------------------------------------------------------------------------*/
110 void
invalidate_simul_efuns(void)111 invalidate_simul_efuns (void)
112
113 /* Invalidate all simul_efun information - usually because the
114 * object is destructed.
115 */
116
117 {
118 simul_efun_table_t *entry;
119 ident_t *id;
120 int i, j;
121
122 /* Invalidate the simul_efun table */
123 for (entry = simul_efun_table, i = SIZE_SEFUN_TABLE; --i >= 0; )
124 {
125 entry->funstart = NULL;
126 entry++;
127 }
128
129 /* Remove all sefun shadows for efuns.
130 * If they are listed in the table, move then into the inactive list.
131 */
132 for (id = all_efuns; id; id = id->next_all)
133 {
134 j = id->u.global.sim_efun;
135 if ((size_t)j < SIZE_SEFUN_TABLE)
136 {
137 simul_efunp[j].offset.func = all_discarded_simul_efun;
138 all_discarded_simul_efun = j;
139 }
140 id->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER;
141 }
142
143 /* Mark all simulefun identifier entries as non-existing
144 * and move them into the inactive list.
145 */
146 while (all_simul_efuns != NULL)
147 {
148 id = all_simul_efuns;
149 j = id->u.global.sim_efun;
150
151 all_simul_efuns = all_simul_efuns->next_all;
152
153 simul_efunp[j].offset.func = all_discarded_simul_efun;
154 all_discarded_simul_efun = j;
155
156 free_shared_identifier(id);
157 }
158
159 /* Free the old program and vector, if any */
160 if (simul_efun_program)
161 {
162 free_prog(simul_efun_program, MY_TRUE);
163 simul_efun_program = NULL;
164 }
165
166 if (simul_efun_vector)
167 {
168 free_array(simul_efun_vector);
169 simul_efun_vector = NULL;
170 }
171 } /* invalidate_simul_efuns() */
172
173 /*-------------------------------------------------------------------------*/
174 Bool
assert_simul_efun_object(void)175 assert_simul_efun_object (void)
176
177 /* (Re)load the simul_efun object and extract all information we need.
178 * Result is TRUE if either the simul_efun object could be loaded, or if
179 * master::get_simul_efun() did not return a string/string vector to
180 * name the simul efun object. The result is FALSE if master::get_simul_efun()
181 * specified a simul efun object, which couldn't be found.
182 *
183 * In other words: after calling assert_simul_efun_object(), the caller
184 * still has to check if simul_efun_object is NULL.
185 *
186 * At the time of call, simul_efun_object must be NULL.
187 */
188
189 {
190 svalue_t *svp;
191 object_t *ob;
192 program_t *progp;
193 CBool *visible; /* Flag for every function: visible or not */
194 string_t *name;
195 int i, j, num_fun;
196
197 invalidate_simul_efuns(); /* Invalidate the simul_efun information */
198
199 free_defines(); /* to prevent #defines hideing places for globals */
200
201 /* Get the name(s) of the simul_efun object. */
202 svp = apply_master(STR_GET_SEFUN, 0);
203
204 /* If a simul_efun_object appears during the GET_SEFUN call, it
205 * might have been due to a recursive get_simul_efun() call which may
206 * have gotten an old backup copy. This can lead to hard-to-debug
207 * variable and function definition inconsistencies.
208 */
209 if (simul_efun_object)
210 {
211 printf("%s simul_efun object appeared while asking for it.\n", time_stamp());
212 return MY_TRUE;
213 }
214
215 if (svp == NULL)
216 {
217 printf("%s No simul_efun\n", time_stamp());
218 return MY_TRUE;
219 }
220
221 if (svp->type == T_POINTER)
222 {
223 simul_efun_vector = svp->u.vec;
224 svp->type = T_NUMBER;
225 if (VEC_SIZE(svp->u.vec))
226 svp = svp->u.vec->item;
227 }
228
229 if (svp->type != T_STRING)
230 {
231 printf("%s No simul_efun\n", time_stamp());
232 return MY_TRUE;
233 }
234
235 /* Make the (primary) simul_efun name */
236 name = del_slash(svp->u.str);
237 if (simul_efun_file_name)
238 free_mstring(simul_efun_file_name);
239 simul_efun_file_name = make_tabled(name);
240
241 /* Get the object and load the program */
242 ob = find_object(simul_efun_file_name);
243 if (ob == NULL)
244 {
245 fprintf(stderr, "%s The simul_efun file %s was not loaded.\n"
246 , time_stamp(), get_txt(simul_efun_file_name));
247 fprintf(stderr, "%s The function get_simul_efun() in the master must load it.\n"
248 , time_stamp());
249 return MY_FALSE;
250 }
251 if (O_PROG_SWAPPED(ob) && load_ob_from_swap(ob) < 0)
252 {
253 fprintf(stderr, "%s Out of memory (unswap object '%s') ==> "
254 "No simul_efun\n", time_stamp(), get_txt(ob->name));
255 return MY_TRUE;
256 }
257 reference_prog( (simul_efun_program = ob->prog), "get_simul_efun");
258
259 num_fun = ob->prog->num_function_names;
260 if (num_fun == 0)
261 return MY_TRUE;
262 if (!simul_efunp)
263 {
264 simul_efunp = xalloc(sizeof (function_t) * num_fun);
265 }
266 else
267 num_fun = total_simul_efun;
268
269 free_defines(); /* to prevent #defines hideing places for globals */
270
271 /* locals and defines are freed now. There are still reserved words,
272 * but it is impossible to define a function with the name being
273 * a reserved word, thus, there will be no clashes with higher-priority
274 * shared identifiers.
275 */
276
277 progp = ob->prog;
278 visible = alloca((i = ob->prog->num_functions) * sizeof(*visible));
279 memset(visible, 0, i);
280 i = ob->prog->num_function_names;
281 while (--i >= 0)
282 visible[progp->function_names[i]] = MY_TRUE;
283 /* The functions .num_function_names+1 .. .num_functions are not
284 * visible by definition.
285 */
286
287 /* Loop over the functions in the simul_efun object and
288 * copy the salient information.
289 */
290 for (i = 0; i < ob->prog->num_functions; i++)
291 {
292 int ix;
293 funflag_t flags, flags2;
294 fun_hdr_p funstart;
295 mp_int fun_ix_offs, var_ix_offs;
296 program_t *inherit_progp;
297
298 if (!visible[i])
299 continue;
300
301 ix = i;
302 flags2 = flags = progp->functions[ix];
303 flags &= ~FUNSTART_MASK;
304
305 /* Pinpoint the function, resolving inheritance where
306 * necessary.
307 */
308 fun_ix_offs = ix;
309 var_ix_offs = 0;
310 inherit_progp = progp;
311 while (flags2 & NAME_INHERITED)
312 {
313 inherit_t *inheritp;
314
315 inheritp = &inherit_progp->inherit[flags2 & INHERIT_MASK];
316 ix -= inheritp->function_index_offset;
317 var_ix_offs += inheritp->variable_index_offset;
318 inherit_progp = inheritp->prog;
319 flags2 = inherit_progp->functions[ix];
320 }
321 fun_ix_offs -= ix;
322
323 funstart = inherit_progp->program + (flags2 & FUNSTART_MASK);
324
325 /* Don't stumble over undefined functions */
326 if (is_undef_function(funstart))
327 {
328 flags |= NAME_UNDEFINED;
329 }
330
331 /* If the function is __INIT, pretend it's a private function */
332 if ( !(flags & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE|NAME_UNDEFINED)) )
333 {
334 string_t *function_name;
335
336 memcpy( &function_name, FUNCTION_NAMEP(funstart)
337 , sizeof function_name);
338 if (mstreq(function_name, STR_VARINIT))
339 flags |= TYPE_MOD_PRIVATE;
340 }
341
342 /* If the function is indeed visible, get its information */
343 if ( !(flags & (TYPE_MOD_STATIC|TYPE_MOD_PROTECTED|TYPE_MOD_PRIVATE|NAME_UNDEFINED)) )
344 {
345 string_t *function_name;
346 ident_t *p;
347 vartype_t type;
348 unsigned char num_arg, num_locals;
349
350 memcpy( &function_name, FUNCTION_NAMEP(funstart)
351 , sizeof function_name);
352 memcpy(&type, FUNCTION_TYPEP(funstart), sizeof(type));
353 num_arg = FUNCTION_NUM_ARGS(funstart) & 0x7f;
354 num_locals = FUNCTION_NUM_VARS(funstart);
355
356 /* Find or make the identifier for the function */
357 p = make_shared_identifier_mstr(function_name, I_TYPE_GLOBAL, 0);
358 if (p->type == I_TYPE_UNKNOWN)
359 {
360 init_global_identifier(p, /* bVariable: */ MY_FALSE);
361 p->next_all = all_simul_efuns;
362 all_simul_efuns = p;
363 }
364
365 if (flags & TYPE_MOD_VARARGS)
366 num_arg = SIMUL_EFUN_VARARGS;
367
368 /* Find the proper index in simul_efunp[] */
369 switch(0) { default: /* TRY... */
370
371 /* Try to find a discarded sefun entry with matching
372 * arguments to reuse.
373 */
374 if (all_discarded_simul_efun >= 0)
375 {
376 int last;
377
378 j = all_discarded_simul_efun;
379 while ( (j = simul_efunp[last = j].offset.func) >= 0)
380 {
381 if (num_arg != simul_efunp[j].num_arg
382 || 0 != ((simul_efunp[j].flags ^ flags) & TYPE_MOD_XVARARGS)
383 )
384 continue;
385 if (!mstreq(function_name, simul_efunp[j].name))
386 continue;
387
388 /* Found one: remove it from the 'discarded' list */
389 simul_efunp[last].offset.func =
390 simul_efunp[j].offset.func;
391 break;
392 }
393 if (j >= 0)
394 break; /* switch */
395 }
396
397 /* New simul_efun: make a new entry */
398 (void)ref_mstring(function_name);
399 j = num_simul_efun++;
400 if (num_simul_efun > num_fun)
401 {
402 num_fun = num_simul_efun + 12;
403 simul_efunp = rexalloc(simul_efunp
404 , sizeof (function_t) * num_fun
405 );
406 }
407 simul_efunp[j].num_arg = num_arg;
408 } /* switch() */
409
410 /* j now indexes the simul_efunp[] entry to use */
411
412 p->u.global.sim_efun = j;
413 simul_efunp[j].name = function_name;
414 simul_efunp[j].flags = flags;
415 simul_efunp[j].type.typeflags = type.type;
416 #ifdef USE_STRUCTS
417 simul_efunp[j].type.t_struct = type.t_struct;
418 #endif
419
420 /* If possible, make an entry in the simul_efun table */
421 if ((size_t)j < SEFUN_TABLE_SIZE)
422 {
423 simul_efun_table[j].funstart = funstart;
424 simul_efun_table[j].program = inherit_progp;
425 simul_efun_table[j].function_index_offset = fun_ix_offs;
426 simul_efun_table[j].variable_index_offset = var_ix_offs;
427 }
428 } /* if (function visible) */
429 } /* for ( all functions) */
430
431 total_simul_efun = num_fun;
432 simul_efun_object = ob;
433
434 return MY_TRUE;
435 } /* get_simul_efun_object() */
436
437 /*-------------------------------------------------------------------------*/
438 string_t *
query_simul_efun_file_name(void)439 query_simul_efun_file_name(void)
440
441 /* Return the name of the primary simul_efun object.
442 * Result is a tabled string, but no extra reference is added.
443 */
444
445 {
446 #ifdef DEBUG
447 if (simul_efunp == NULL)
448 fatal("query_simul_efun_file_name called when non exists!\n");
449 #endif
450 return simul_efun_file_name;
451 }
452
453 /*-------------------------------------------------------------------------*/
454 #ifdef GC_SUPPORT
455
456 void
clear_simul_efun_refs(void)457 clear_simul_efun_refs (void)
458
459 /* GC support: clear the references of all memory held by the module.
460 */
461
462 {
463 if (simul_efun_vector && simul_efun_vector->ref)
464 {
465 simul_efun_vector->ref = 0;
466 clear_ref_in_vector(
467 simul_efun_vector->item,
468 VEC_SIZE(simul_efun_vector)
469 );
470 }
471 if (simul_efun_program)
472 simul_efun_program->ref = 0;
473 } /* clear_simul_efun_refs() */
474
475 /*-------------------------------------------------------------------------*/
476 void
count_simul_efun_refs(void)477 count_simul_efun_refs (void)
478
479 /* GC support: count the references of all memory held by the module.
480 */
481
482 {
483 if (simul_efun_file_name)
484 count_ref_from_string(simul_efun_file_name);
485
486 if (simul_efunp)
487 {
488 int i;
489
490 note_malloced_block_ref((char *)simul_efunp);
491 for (i = num_simul_efun; --i >= 0; )
492 count_ref_from_string(simul_efunp[i].name);
493 }
494
495 if (simul_efun_vector && !simul_efun_vector->ref++)
496 {
497 note_malloced_block_ref((char *)simul_efun_vector);
498 count_ref_in_vector(
499 simul_efun_vector->item,
500 VEC_SIZE(simul_efun_vector)
501 );
502 }
503 if (simul_efun_program)
504 mark_program_ref(simul_efun_program);
505 } /* count_simul_efun_refs() */
506
507 #endif /* GC_SUPPORT */
508
509 /*-------------------------------------------------------------------------*/
510 #ifdef DEBUG
511
512 void
count_simul_efun_extra_refs(struct pointer_table * ptable)513 count_simul_efun_extra_refs (struct pointer_table *ptable)
514
515 /* DEBUG support: count the extra refs for structures of this module.
516 */
517
518 {
519 if (simul_efun_vector)
520 {
521 simul_efun_vector->extra_ref++;
522 if (NULL != register_pointer(ptable, simul_efun_vector) )
523 count_extra_ref_in_vector(
524 simul_efun_vector->item,
525 VEC_SIZE(simul_efun_vector)
526 );
527 }
528
529 if (simul_efun_program)
530 {
531 simul_efun_program->extra_ref++;
532 if (NULL == register_pointer(ptable, simul_efun_program))
533 return;
534 simul_efun_program->extra_ref = 1;
535 count_inherits(simul_efun_program);
536 }
537 } /* count_simul_efun_extra_refs() */
538
539 #endif
540
541 /*-------------------------------------------------------------------------*/
542 #if 0
543 /*
544 * Test if 'name' is a simul_efun. The string pointer MUST be a pointer to
545 * a shared string.
546 */
547 function_t *find_simul_efun(name)
548 char *name;
549 {
550 int i;
551 for (i=0; i < num_simul_efun; i++) {
552 if (name == simul_efunp[i].name)
553 return &simul_efunp[i];
554 }
555 return 0;
556 } /* find_simul_efun() */
557 #endif
558
559 /***************************************************************************/
560
561