1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/tads3/vmbif.cpp,v 1.3 1999/07/11 00:46:59 MJRoberts Exp $";
4 #endif
5 
6 /*
7  *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
8  *
9  *   Please see the accompanying license file, LICENSE.TXT, for information
10  *   on using and copying this software.
11  */
12 /*
13 Name
14   vmbif.cpp - built-in function set table implementation
15 Function
16 
17 Notes
18 
19 Modified
20   12/05/98 MJRoberts  - Creation
21 */
22 
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "t3std.h"
27 #include "utf8.h"
28 #include "charmap.h"
29 #include "vmtype.h"
30 #include "vmerr.h"
31 #include "vmerrnum.h"
32 #include "vmglob.h"
33 #include "vmbif.h"
34 #include "vmbifreg.h"
35 #include "vmstr.h"
36 #include "vmobj.h"
37 #include "vmrun.h"
38 
39 
40 /* ------------------------------------------------------------------------ */
41 /*
42  *   Create the function set table with a given number of initial entries
43  */
CVmBifTable(size_t init_entries)44 CVmBifTable::CVmBifTable(size_t init_entries)
45 {
46     /* allocate space for our entries */
47     if (init_entries != 0)
48     {
49         /* allocate the space */
50         table_ = (vm_bif_entry_t **)
51                  t3malloc(init_entries * sizeof(table_[0]));
52         names_ = (char **)
53                  t3malloc(init_entries * sizeof(names_[0]));
54     }
55     else
56     {
57         /* we have no entries */
58         table_ = 0;
59         names_ = 0;
60     }
61 
62     /* no entries are defined yet */
63     count_ = 0;
64 
65     /* remember the allocation size */
66     alloc_ = init_entries;
67 }
68 
69 /* ------------------------------------------------------------------------ */
70 /*
71  *   Delete the table
72  */
~CVmBifTable()73 CVmBifTable::~CVmBifTable()
74 {
75     /* free the table, if we ever allocated one */
76     if (table_ != 0)
77         t3free(table_);
78 
79     /* free the function set names list, if we allocated it */
80     if (names_ != 0)
81     {
82         /* clear the table to delete the entries in the 'names_' array */
83         clear();
84 
85         /* free the table */
86         t3free(names_);
87     }
88 }
89 
90 /* ------------------------------------------------------------------------ */
91 /*
92  *   Clear all entries from the table
93  */
clear()94 void CVmBifTable::clear()
95 {
96     /* delete the 'names' entries, if we allocated any */
97     if (names_ != 0)
98     {
99         size_t i;
100 
101         /* free each element */
102         for (i = 0 ; i < count_ ; ++i)
103             lib_free_str(names_[i]);
104     }
105 
106     /*
107      *   Reset the entry counter.  Note that this doesn't affect any
108      *   allocation; we keep a separate count of the number of table slots
109      *   we have allocated.  Table slots don't have any additional
110      *   associated memory, so we don't need to worry about cleaning
111      *   anything up at this point.
112      */
113     count_ = 0;
114 }
115 
116 /* ------------------------------------------------------------------------ */
117 /*
118  *   Ensure that we have space for a given number of entries
119  */
ensure_space(size_t entries,size_t increment)120 void CVmBifTable::ensure_space(size_t entries, size_t increment)
121 {
122     /* if we don't have enough space, allocate more */
123     if (entries >= alloc_)
124     {
125         size_t new_table_size;
126         size_t new_names_size;
127 
128         /* increase the allocation size by the given increment */
129         alloc_ += increment;
130 
131         /* if it's still too small, bump it up to the required size */
132         if (alloc_ < entries)
133             alloc_ = entries;
134 
135         /* compute the new sizes */
136         new_table_size = alloc_ * sizeof(table_[0]);
137         new_names_size = alloc_ * sizeof(names_[0]);
138 
139         /*
140          *   if we have a table already, reallocate it at the larger size;
141          *   otherwise, allocate a new table
142          */
143         if (table_ != 0)
144         {
145             table_ = (vm_bif_entry_t **)t3realloc(table_, new_table_size);
146             names_ = (char **)t3realloc(names_, new_names_size);
147         }
148         else
149         {
150             table_ = (vm_bif_entry_t **)t3malloc(new_table_size);
151             names_ = (char **)t3malloc(alloc_ * new_names_size);
152         }
153     }
154 }
155 
156 /* ------------------------------------------------------------------------ */
157 /*
158  *   Add an entry to the table
159  */
add_entry(const char * func_set_id)160 void CVmBifTable::add_entry(const char *func_set_id)
161 {
162     vm_bif_entry_t *entry;
163     const char *vsn;
164     size_t name_len;
165 
166     /* ensure we have space for one more entry */
167     ensure_space(count_ + 1, 5);
168 
169     /* find the version suffix in the name, if any */
170     vsn = lib_find_vsn_suffix(func_set_id, '/', "000000", &name_len);
171 
172     /* look up the function set by name */
173     for (entry = G_bif_reg_table ; entry->func_set_id != 0 ; ++entry)
174     {
175         const char *entry_vsn;
176         size_t entry_name_len;
177 
178         /* find the version number in this entry */
179         entry_vsn = lib_find_vsn_suffix(entry->func_set_id, '/', "000000",
180                                         &entry_name_len);
181 
182         /* see if this is a match */
183         if (name_len == entry_name_len
184             && memcmp(func_set_id, entry->func_set_id, name_len) == 0)
185         {
186             /*
187              *   make sure the version provided in the VM is at least as
188              *   high as the requested version
189              */
190             if (strcmp(vsn, entry_vsn) > 0)
191                 err_throw_a(VMERR_FUNCSET_TOO_OLD, 2,
192                             ERR_TYPE_TEXTCHAR, func_set_id,
193                             ERR_TYPE_TEXTCHAR, entry_vsn);
194 
195             /*
196              *   It's a match - add the new entry.  Simply keep a pointer
197              *   to the static table entry.
198              */
199             table_[count_] = entry;
200 
201             /* store the new name element as well */
202             names_[count_] = lib_copy_str(func_set_id);
203 
204             /* count the new entry */
205             ++count_;
206 
207             /* we're done */
208             return;
209         }
210     }
211 
212     /* we didn't find it - handle it according to our resolution mode */
213     add_entry_unresolved(func_set_id);
214 }
215 
216 /* ------------------------------------------------------------------------ */
217 /*
218  *   Function Set helper functions
219  */
220 
221 /*
222  *   check arguments; throws an error if the argument count doesn't match
223  *   the given value
224  */
check_argc(VMG_ uint argc,uint needed_argc)225 void CVmBif::check_argc(VMG_ uint argc, uint needed_argc)
226 {
227     if (argc != needed_argc)
228         err_throw(VMERR_WRONG_NUM_OF_ARGS);
229 }
230 
231 /*
232  *   check arguments; throws an error if the argument count is outside of
233  *   the given range
234  */
check_argc_range(VMG_ uint argc,uint argc_min,uint argc_max)235 void CVmBif::check_argc_range(VMG_ uint argc, uint argc_min, uint argc_max)
236 {
237     if (argc < argc_min || argc > argc_max)
238         err_throw(VMERR_WRONG_NUM_OF_ARGS);
239 }
240 
241 /*
242  *   return a string value
243  */
retval_str(VMG_ const char * str)244 void CVmBif::retval_str(VMG_ const char *str)
245 {
246     retval_str(vmg_ str, strlen(str));
247 }
248 
retval_str(VMG_ const char * str,size_t len)249 void CVmBif::retval_str(VMG_ const char *str, size_t len)
250 {
251     vm_obj_id_t str_obj;
252 
253     /* create a string to hold the return value */
254     str_obj = CVmObjString::create(vmg_ FALSE, str, len);
255 
256     /* return the string */
257     retval_obj(vmg_ str_obj);
258 }
259 
260 /*
261  *   return an object value
262  */
retval_obj(VMG_ vm_obj_id_t obj)263 void CVmBif::retval_obj(VMG_ vm_obj_id_t obj)
264 {
265     if (obj == VM_INVALID_OBJ)
266         G_interpreter->get_r0()->set_nil();
267     else
268         G_interpreter->get_r0()->set_obj(obj);
269 }
270 
271 /*
272  *   return a property value
273  */
retval_prop(VMG_ vm_prop_id_t prop)274 void CVmBif::retval_prop(VMG_ vm_prop_id_t prop)
275 {
276     G_interpreter->get_r0()->set_propid(prop);
277 }
278 
279 /*
280  *   return an integer value
281  */
retval_int(VMG_ long val)282 void CVmBif::retval_int(VMG_ long val)
283 {
284     G_interpreter->get_r0()->set_int(val);
285 }
286 
287 /*
288  *   return true
289  */
retval_true(VMG0_)290 void CVmBif::retval_true(VMG0_)
291 {
292     G_interpreter->get_r0()->set_true();
293 }
294 
295 /*
296  *   return nil
297  */
retval_nil(VMG0_)298 void CVmBif::retval_nil(VMG0_)
299 {
300     G_interpreter->get_r0()->set_nil();
301 }
302 
303 /*
304  *   return a boolean value - nil if false, true if true
305  */
retval_bool(VMG_ int val)306 void CVmBif::retval_bool(VMG_ int val)
307 {
308     G_interpreter->get_r0()->set_logical(val);
309 }
310 
311 /*
312  *   return a function pointer value
313  */
retval_fnptr(VMG_ pool_ofs_t ofs)314 void CVmBif::retval_fnptr(VMG_ pool_ofs_t ofs)
315 {
316     G_interpreter->get_r0()->set_fnptr(ofs);
317 }
318 
319 /*
320  *   return a value
321  */
retval(VMG_ const vm_val_t * val)322 void CVmBif::retval(VMG_ const vm_val_t *val)
323 {
324     *G_interpreter->get_r0() = *val;
325 }
326 
327 /* ------------------------------------------------------------------------ */
328 /*
329  *   Pop a string value
330  */
pop_str_val(VMG0_)331 const char *CVmBif::pop_str_val(VMG0_)
332 {
333     vm_val_t val;
334     CVmObject *obj;
335     const char *p;
336 
337     /* pop the value */
338     G_stk->pop(&val);
339 
340     /* see what we have */
341     switch(val.typ)
342     {
343     case VM_SSTRING:
344         /* it's a constant string - get the constant pool pointer */
345         return G_const_pool->get_ptr(val.val.ofs);
346 
347     case VM_OBJ:
348         /* get the object */
349         obj = vm_objp(vmg_ val.val.obj);
350 
351         /* get the string value, if it has one */
352         p = obj->get_as_string(vmg0_);
353 
354         /* if it has a string value, return it */
355         if (p != 0)
356             return p;
357 
358         /* we didn't get a string value */
359         break;
360 
361     default:
362         /* other types don't have a string value */
363         break;
364     }
365 
366     /* if we got here, the value isn't a string */
367     err_throw(VMERR_STRING_VAL_REQD);
368     return 0;
369 }
370 
371 /* ------------------------------------------------------------------------ */
372 /*
373  *   Pop a list value
374  */
pop_list_val(VMG0_)375 const char *CVmBif::pop_list_val(VMG0_)
376 {
377     vm_val_t val;
378     CVmObject *obj;
379     const char *p;
380 
381     /* pop the value */
382     G_stk->pop(&val);
383 
384     /* see what we have */
385     switch(val.typ)
386     {
387     case VM_LIST:
388         /* it's a constant list - get the constant pool pointer */
389         return G_const_pool->get_ptr(val.val.ofs);
390 
391     case VM_OBJ:
392         /* get the object */
393         obj = vm_objp(vmg_ val.val.obj);
394 
395         /* get the list value, if it has one */
396         p = obj->get_as_list();
397 
398         /* if it has a liist value, return it */
399         if (p != 0)
400             return p;
401 
402         /* we didn't get a list value */
403         break;
404 
405     default:
406         /* other types don't have a list value */
407         break;
408     }
409 
410     /* if we got here, the value isn't a list */
411     err_throw(VMERR_LIST_VAL_REQD);
412     return 0;
413 }
414 
415 /* ------------------------------------------------------------------------ */
416 /*
417  *   Pop a string into a buffer, and null-terminate the result.
418  */
pop_str_val_buf(VMG_ char * buf,size_t buflen)419 void CVmBif::pop_str_val_buf(VMG_ char *buf, size_t buflen)
420 {
421     const char *strp;
422     size_t copy_len;
423 
424     /* pop the string value */
425     strp = pop_str_val(vmg0_);
426 
427     /*
428      *   get the length, but limit it to our buffer size, less one byte
429      *   for null termination
430      */
431     copy_len = vmb_get_len(strp);
432     if (copy_len > buflen - 1)
433         copy_len = utf8_ptr::s_trunc(strp + VMB_LEN, buflen - 1);
434 
435     /* copy the string */
436     memcpy(buf, strp + VMB_LEN, copy_len);
437 
438     /* null-terminate the result */
439     buf[copy_len] = '\0';
440 }
441 
442 
443 /* ------------------------------------------------------------------------ */
444 /*
445  *   Pop a string into a buffer, translating the string into the filename
446  *   character set and null-terminating the result.
447  */
pop_str_val_fname(VMG_ char * buf,size_t buflen)448 void CVmBif::pop_str_val_fname(VMG_ char *buf, size_t buflen)
449 {
450     const char *strp;
451     size_t copy_len;
452 
453     /* pop the string value */
454     strp = pop_str_val(vmg0_);
455 
456     /* get the length */
457     copy_len = vmb_get_len(strp);
458 
459     /*
460      *   map it into the local filename character set and store the result
461      *   in the output buffer - reserve one byte for the null termination
462      *   byte
463      */
464     copy_len = G_cmap_to_fname->map_utf8(buf, buflen - 1,
465                                          strp + VMB_LEN, copy_len, 0);
466 
467     /* null-terminate the result */
468     buf[copy_len] = '\0';
469 }
470 
471 /*
472  *   Pop a string into a buffer, translating the string into the user
473  *   interface character set and null-terminating the result.
474  */
pop_str_val_ui(VMG_ char * buf,size_t buflen)475 void CVmBif::pop_str_val_ui(VMG_ char *buf, size_t buflen)
476 {
477     const char *strp;
478     size_t copy_len;
479 
480     /* pop the string value */
481     strp = pop_str_val(vmg0_);
482 
483     /* get the length */
484     copy_len = vmb_get_len(strp);
485 
486     /*
487      *   map it into the local UI character set and store the result in
488      *   the output buffer - reserve one byte for the null termination
489      *   byte
490      */
491     copy_len = G_cmap_to_ui->map_utf8(buf, buflen - 1,
492                                       strp + VMB_LEN, copy_len, 0);
493 
494     /* null-terminate the result */
495     buf[copy_len] = '\0';
496 }
497 
498 /* ------------------------------------------------------------------------ */
499 /*
500  *   Pop an integer value
501  */
pop_int_val(VMG0_)502 int CVmBif::pop_int_val(VMG0_)
503 {
504     vm_val_t val;
505 
506     /* pop a number */
507     G_interpreter->pop_int(vmg_ &val);
508 
509     /* return the value */
510     return (int)val.val.intval;
511 }
512 
513 /*
514  *   Pop a long integer value
515  */
pop_long_val(VMG0_)516 int CVmBif::pop_long_val(VMG0_)
517 {
518     vm_val_t val;
519 
520     /* pop a number */
521     G_interpreter->pop_int(vmg_ &val);
522 
523     /* return the value */
524     return val.val.intval;
525 }
526 
527 /*
528  *   Pop a true/nil logical value
529  */
pop_bool_val(VMG0_)530 int CVmBif::pop_bool_val(VMG0_)
531 {
532     vm_val_t val;
533 
534     /* pop a value */
535     G_stk->pop(&val);
536 
537     /* check the type */
538     switch(val.typ)
539     {
540     case VM_NIL:
541         /* nil - interpret this as false */
542         return FALSE;
543 
544     case VM_TRUE:
545         /* true */
546         return TRUE;
547 
548     case VM_INT:
549         /* integer - return true if it's nonzero */
550         return (val.val.intval != 0);
551 
552     default:
553         /* anything else is unacceptable */
554         err_throw(VMERR_BAD_TYPE_BIF);
555 
556         /*
557          *   (for the compiler's benefit, which doesn't know err_throw
558          *   doesn't return and thus might want to warn about our failure to
559          *   return a value)
560          */
561         return FALSE;
562     }
563 }
564 
565 /*
566  *   Pop an object reference value
567  */
pop_obj_val(VMG0_)568 vm_obj_id_t CVmBif::pop_obj_val(VMG0_)
569 {
570     vm_val_t val;
571 
572     /* pop an object reference */
573     G_interpreter->pop_obj(vmg_ &val);
574 
575     /* return the value */
576     return val.val.obj;
577 }
578 
579 /*
580  *   Pop a property ID value
581  */
pop_propid_val(VMG0_)582 vm_prop_id_t CVmBif::pop_propid_val(VMG0_)
583 {
584     vm_val_t val;
585 
586     /* pop a property ID */
587     G_interpreter->pop_prop(vmg_ &val);
588 
589     /* return the value */
590     return val.val.prop;
591 }
592 
593