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